From f96fb832e697e3b3d843bb2e786d3cd47bb0e022 Mon Sep 17 00:00:00 2001 From: abrichr Date: Sat, 24 Oct 2026 19:46:34 -0400 Subject: [PATCH 01/13] duplicate check: ask the system of record for the patient (FHIR, REST; SQL fallback) no_duplicate_candidate stopped every new patient who shares a last name or a date of birth with any chart. When the deployment's effects read a FHIR server or OpenEMR's patient API, the check now searches the record channel (every page, fail closed) and stops only on an exact or close match. The on-screen list stays the fallback and runtime.duplicate_check: screen keeps it. Offline on the soak's seed-7 stream: false stops per 100 items 44.8 (screen) against 0.4 (record), the same 12 of 110 true duplicates missed by both. Co-Authored-By: Claude Opus 5.5 --- CAPABILITIES.md | 1 + llms-full.txt | 1 + packages/flow/benchmark/dup_check/README.md | 68 + packages/flow/benchmark/dup_check/__init__.py | 0 packages/flow/benchmark/dup_check/measure.py | 290 + .../dup_check/results/seed7-500.json | 4753 +++++++++++++++++ .../dup_check/results/soak-seed7-55.json | 1102 ++++ packages/flow/docs/DUPLICATE_CHECK.md | 26 + packages/flow/openadapt_flow/deployment.py | 22 + .../openadapt_flow/runtime/checks/__init__.py | 14 + .../runtime/checks/duplicate_record.py | 626 +++ .../flow/openadapt_flow/runtime/replayer.py | 54 +- .../flow/tests/test_duplicate_record_check.py | 296 + 13 files changed, 7251 insertions(+), 2 deletions(-) create mode 100644 packages/flow/benchmark/dup_check/README.md create mode 100644 packages/flow/benchmark/dup_check/__init__.py create mode 100644 packages/flow/benchmark/dup_check/measure.py create mode 100644 packages/flow/benchmark/dup_check/results/seed7-500.json create mode 100644 packages/flow/benchmark/dup_check/results/soak-seed7-55.json create mode 100644 packages/flow/openadapt_flow/runtime/checks/__init__.py create mode 100644 packages/flow/openadapt_flow/runtime/checks/duplicate_record.py create mode 100644 packages/flow/tests/test_duplicate_record_check.py diff --git a/CAPABILITIES.md b/CAPABILITIES.md index f66900f16..bfefe6f42 100644 --- a/CAPABILITIES.md +++ b/CAPABILITIES.md @@ -229,6 +229,7 @@ improvising or retrying when something is off. | Repair by default: once a repair agent is configured, every stop that wrote nothing goes to it without anyone starting it; a stop after a possible write goes to reconciliation; every stop is a row in a ledger with the funnel from stop to resumed item | On main ([#1360](https://github.com/OpenAdaptAI/OpenAdapt/pull/1360)) | Synthetic (MockMed, 22 drift modes, real Claude agent: 6 of 21 repairable stops resumed and verified, 0 wrong saves in 66 runs); routing over recorded OpenEMR stops; live openIMIS relabel: 3 of 3 reached repair, 0 resumed; silent-saves 1.1.0 UI-change blocks: 18 of 28 items that always stopped now correct, 0 wrong | `run` (on by default, `--no-auto-repair`), `repair:` in `deploy.yaml`, [benchmark/auto_repair](packages/flow/benchmark/auto_repair/README.md) | | Repair a relabeled save button: prove from the recorded identity hash that only the label changed (same control id, kind and form), re-qualify the fix on the new release, and have a person admit the certified copy afresh, since a save button's identity is part of the record check | On main | Real app (local OpenEMR, synthetic data): after the save button was relabeled, the stopped item re-ran and saved once after 8.3 operator minutes and 763 s of re-qualification; 896 s from stop to done ([#1513](https://github.com/OpenAdaptAI/OpenAdapt/pull/1513)) | `openadapt_flow/repair/relabel.py`, [SELF_REPAIR.md](packages/flow/docs/SELF_REPAIR.md#a-relabeled-control-proof-that-only-the-label-changed) | | A duplicate check that reads the app's match list (`no_duplicate_candidate`): stop only on a listed record with the item's last name or date of birth, fail closed on an unreadable or unloaded list | On main | Real app (local OpenEMR, synthetic data): false stops per 100 items 70.4 before, 16.4 after; 0 silent wrongs in 30 saves ([#1513](https://github.com/OpenAdaptAI/OpenAdapt/pull/1513)) | [DUPLICATE_CHECK.md](packages/flow/docs/DUPLICATE_CHECK.md) | +| A duplicate check that asks the system of record (FHIR `Patient` search or OpenEMR's patient API, every page; SQL as a documented fallback): stop on an exact or close match, proceed otherwise, fail closed on any read problem. The default when `effects` reads FHIR or OpenEMR's patient API | Open PR | Offline on the soak's seed-7 stream with planted true duplicates: false stops per 100 items 44.8 (screen) against 0.4 (record) on 563 items; 12 of 110 true duplicates missed by both (a married name with a mistyped date of birth). Not yet run on live OpenEMR | [DUPLICATE_CHECK.md](packages/flow/docs/DUPLICATE_CHECK.md) | | A switched-off `problems_only` gate asks a person before the save instead of stopping the item | On main | Synthetic; real app in [#1513](https://github.com/OpenAdaptAI/OpenAdapt/pull/1513) | [APPROVAL_MODES.md](packages/flow/docs/APPROVAL_MODES.md#when-the-mode-turns-itself-off) | | Fleet drift patches: a verified fix with no patient data, shared across sites under policy | Released, local reference hub | Simulator | `openadapt-flow fleet`, [FLEET_DRIFT_PATCHES.md](packages/flow/docs/FLEET_DRIFT_PATCHES.md) | diff --git a/llms-full.txt b/llms-full.txt index acb6358d3..ccf95dda9 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -626,6 +626,7 @@ improvising or retrying when something is off. | Repair by default: once a repair agent is configured, every stop that wrote nothing goes to it without anyone starting it; a stop after a possible write goes to reconciliation; every stop is a row in a ledger with the funnel from stop to resumed item | On main ([#1360](https://github.com/OpenAdaptAI/OpenAdapt/pull/1360)) | Synthetic (MockMed, 22 drift modes, real Claude agent: 6 of 21 repairable stops resumed and verified, 0 wrong saves in 66 runs); routing over recorded OpenEMR stops; live openIMIS relabel: 3 of 3 reached repair, 0 resumed; silent-saves 1.1.0 UI-change blocks: 18 of 28 items that always stopped now correct, 0 wrong | `run` (on by default, `--no-auto-repair`), `repair:` in `deploy.yaml`, [benchmark/auto_repair](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/benchmark/auto_repair/README.md) | | Repair a relabeled save button: prove from the recorded identity hash that only the label changed (same control id, kind and form), re-qualify the fix on the new release, and have a person admit the certified copy afresh, since a save button's identity is part of the record check | On main | Real app (local OpenEMR, synthetic data): after the save button was relabeled, the stopped item re-ran and saved once after 8.3 operator minutes and 763 s of re-qualification; 896 s from stop to done ([#1513](https://github.com/OpenAdaptAI/OpenAdapt/pull/1513)) | `openadapt_flow/repair/relabel.py`, [SELF_REPAIR.md](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/SELF_REPAIR.md#a-relabeled-control-proof-that-only-the-label-changed) | | A duplicate check that reads the app's match list (`no_duplicate_candidate`): stop only on a listed record with the item's last name or date of birth, fail closed on an unreadable or unloaded list | On main | Real app (local OpenEMR, synthetic data): false stops per 100 items 70.4 before, 16.4 after; 0 silent wrongs in 30 saves ([#1513](https://github.com/OpenAdaptAI/OpenAdapt/pull/1513)) | [DUPLICATE_CHECK.md](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/DUPLICATE_CHECK.md) | +| A duplicate check that asks the system of record (FHIR `Patient` search or OpenEMR's patient API, every page; SQL as a documented fallback): stop on an exact or close match, proceed otherwise, fail closed on any read problem. The default when `effects` reads FHIR or OpenEMR's patient API | Open PR | Offline on the soak's seed-7 stream with planted true duplicates: false stops per 100 items 44.8 (screen) against 0.4 (record) on 563 items; 12 of 110 true duplicates missed by both (a married name with a mistyped date of birth). Not yet run on live OpenEMR | [DUPLICATE_CHECK.md](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/DUPLICATE_CHECK.md) | | A switched-off `problems_only` gate asks a person before the save instead of stopping the item | On main | Synthetic; real app in [#1513](https://github.com/OpenAdaptAI/OpenAdapt/pull/1513) | [APPROVAL_MODES.md](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/APPROVAL_MODES.md#when-the-mode-turns-itself-off) | | Fleet drift patches: a verified fix with no patient data, shared across sites under policy | Released, local reference hub | Simulator | `openadapt-flow fleet`, [FLEET_DRIFT_PATCHES.md](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/FLEET_DRIFT_PATCHES.md) | diff --git a/packages/flow/benchmark/dup_check/README.md b/packages/flow/benchmark/dup_check/README.md new file mode 100644 index 000000000..6bcecd554 --- /dev/null +++ b/packages/flow/benchmark/dup_check/README.md @@ -0,0 +1,68 @@ +# Duplicate check: screen against record, offline + +In the production soak (#1513), the duplicate check was the largest operator +cost after approvals. It stops a new patient who shares a last name or date +of birth with an existing chart. This measures the check that asks the system +of record instead (`runtime/checks/duplicate_record.py`) against the screen +rule, on the same stream and the same patient table. + +```bash +python -m benchmark.dup_check.measure # writes results/*.json +``` + +No app, no model calls. `measure.py` says exactly what each check sees. + +## The streams + +- `soak-seed7-55`: the 55 items the soak ran (`lifecycle/results/production-2026-10-24/after/workload.jsonl`), with its 12 existing charts. +- `seed7-500`: 500 items from the same generator and seed (`lifecycle.workload.generate(7, 500, 10)`). + +Planted into each, for every existing chart, are true duplicates the soak +lacks: +- the same person under a married name; +- the same person under a hyphenated name; +- the same person with day and month swapped (when that is a valid date). + +Three more kinds were added after the record check's rules were written, as +a held-out set: a typo in the first name, a typo in the last name, and a +married name plus a mistyped day of birth. + +Each item meets the existing charts, every earlier item's right outcome from +the soak's truth, and another clerk's chart for `concurrent_same`. + +## Results (2026-10-24) + +| stream | items | new-chart items | true duplicates | screen: false stops per 100 items | record: false stops per 100 items | screen: missed | record: missed | +|---|---:|---:|---:|---:|---:|---:|---:| +| soak-seed7-55 + planted | 118 | 45 | 71 | 8.5 (10) | 0.0 (0) | 12 | 12 | +| seed7-500 + planted | 563 | 440 | 110 | 44.8 (252) | 0.4 (2) | 12 | 12 | + +- **Screen false stops** are new patients who share a last name or date of + birth with any chart, including charts earlier items created. On the long + stream, 151 of 252 are `clean` items that met a namesake: the 300-surname + pool fills up. +- **Record false stops:** 2, both `shared_dob` items whose first name equals + that of the chart with the same date of birth. The married-name rule stops + them for a person, by design. +- **Missed, both checks:** all 12 are the held-out `heldout_married_dob_typo`. + A married name with a mistyped date of birth shares neither the last name + nor the date of birth with the chart, so neither check sees it. The other + held-out kinds (first-name and last-name typos) were caught by the record + check, and by the screen through the shared date of birth. +- **Record decisions** on the long stream: 450 `none`, 37 `exact`, 63 + `close`, 13 `unreadable`. The `unreadable` ones are the 13 referrals with + no date of birth, a right stop. + +## Limits + +- Offline: the table has FHIR string-search semantics (a case-insensitive + last-name prefix, an exact birth date). Not yet run against live OpenEMR's + FHIR or REST search; that run needs the fixture slot (`dup-check-sor` in + the queue). The paging and fail-closed behavior is tested against a real + local HTTP server (`tests/test_duplicate_record_check.py`). +- The close-match rules were written with the soak's generator in view + (`ambiguous_same_person` changes the day of birth). Only the held-out kinds + are a test of rules not tuned to them. +- One seed. The screen rule is the `no_duplicate_candidate` rule applied to + OpenEMR's listing as the soak describes it (any chart sharing the first + name, the last name or the date of birth); it is not a run of the browser. diff --git a/packages/flow/benchmark/dup_check/__init__.py b/packages/flow/benchmark/dup_check/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/packages/flow/benchmark/dup_check/measure.py b/packages/flow/benchmark/dup_check/measure.py new file mode 100644 index 000000000..75dcfb3b8 --- /dev/null +++ b/packages/flow/benchmark/dup_check/measure.py @@ -0,0 +1,290 @@ +"""Screen duplicate check against record duplicate check, offline, on the soak's stream. + + python -m benchmark.dup_check.measure --out DIR + +The stream is the production soak's seed-7 workload (#1513): the committed +55-item stream it ran, and a longer seed-7 stream from the same generator. +Planted into each are true duplicates the soak lacks: an existing patient +referred again under a married name, under a hyphenated name, and with day +and month swapped. + +The patient table each item meets is the existing charts plus every earlier +item's right outcome (the soak's truth), plus another clerk's chart for +``concurrent_same``. So both checks see the same records, and only the check +differs. + +- **screen**: the rule ``no_duplicate_candidate`` applies to OpenEMR's + possible-duplicate list (``production_arm.with_duplicate_list_check``): + OpenEMR lists every chart that shares the first name, the last name or the + date of birth, and the check stops when a listed row carries every word of + the run's last name or shows its date of birth. +- **record**: ``runtime.checks.duplicate_record.check_duplicate`` over the + same table with FHIR search semantics; ``exact`` and ``close`` stop. + +A *false stop* is a stop on an item whose right outcome is a new chart. A +*missed duplicate* is a proceed on an item whose right outcome is no new +chart because the patient already has one. Dates are converted to ISO first, +as intake does since #1514. +""" + +from __future__ import annotations + +import argparse +import json +import random +import re +from dataclasses import dataclass +from pathlib import Path +from typing import Any, Optional + +from benchmark.lifecycle import workload as wl +from openadapt_flow.runtime.checks.duplicate_record import ( + PatientRecord, + TableSource, + check_duplicate, +) + +HERE = Path(__file__).resolve().parent +SOAK = HERE.parent / "lifecycle" / "results" / "production-2026-10-24" / "after" +#: Right outcome "no new chart" because the person already has one. +DUPLICATE_KINDS = { + "dup_existing", + "dup_resend", + "ambiguous_same_person", + "concurrent_same", + "planted_married", + "planted_hyphenated", + "planted_swapped_dob", + "heldout_first_typo", + "heldout_last_typo", + "heldout_married_dob_typo", +} +MARRIED = ("Okafor", "Lindqvist", "Brennan", "Moreau", "Castellanos", "Whitcombe") + + +@dataclass +class Item: + item_id: str + exception: str + params: dict[str, str] + #: The chart the right outcome creates, or None. + intended: Optional[dict[str, str]] + #: Charts that appear just before the item (another clerk's). + before: list[dict[str, str]] + + +def iso(value: str) -> str: + m = re.fullmatch(r"(\d{2})/(\d{2})/(\d{4})", value or "") + return f"{m.group(3)}-{m.group(1)}-{m.group(2)}" if m else (value or "") + + +def from_ledger(path: Path) -> tuple[list[dict[str, str]], list[Item]]: + existing, items = [], [] + for line in path.read_text().splitlines(): + row = json.loads(line) + if row["type"] == "existing_chart": + existing.append({k: v for k, v in row.items() if k != "type"}) + elif row["type"] == "item": + t = row["truth"] + before = [ + i["person"] + for i in t["inject"] + if i.get("event") == "other_clerk_creates" + ] + items.append( + Item( + row["item_id"], t["exception"], row["params"], t["intended"], before + ) + ) + return existing, items + + +def from_generator( + seed: int, n: int, days: int +) -> tuple[list[dict[str, str]], list[Item]]: + w = wl.generate(seed, n, days) + items = [] + for it in w.items: + t = w.truth[it.item_id] + before = [ + i["person"] for i in t.inject if i.get("event") == "other_clerk_creates" + ] + items.append(Item(it.item_id, t.exception, dict(it.params), t.intended, before)) + return list(w.existing), items + + +def plant(existing: list[dict[str, str]], items: list[Item], seed: int) -> list[Item]: + """Insert true duplicates of existing patients at seeded positions.""" + + rng = random.Random(seed) + planted: list[Item] = [] + for n, person in enumerate(existing): + married = dict(person, lname=MARRIED[n % len(MARRIED)]) + planted.append(Item(f"plant-m-{n}", "planted_married", married, None, [])) + hyph = dict( + person, lname=f"{person['lname']}-{MARRIED[(n + 1) % len(MARRIED)]}" + ) + planted.append(Item(f"plant-h-{n}", "planted_hyphenated", hyph, None, [])) + y, m, d = person["DOB"].split("-") + if int(d) <= 12 and d != m: + planted.append( + Item( + f"plant-s-{n}", + "planted_swapped_dob", + dict(person, DOB=f"{y}-{d}-{m}"), + None, + [], + ) + ) + # Held out: variants the record check's rules were not written for. + typo = lambda s: s[:-2] + s[-1] + s[-2] if len(s) > 3 else s + "e" # noqa: E731 + planted.append( + Item( + f"held-f-{n}", + "heldout_first_typo", + dict(person, fname=typo(person["fname"])), + None, + [], + ) + ) + planted.append( + Item( + f"held-l-{n}", + "heldout_last_typo", + dict(person, lname=typo(person["lname"])), + None, + [], + ) + ) + planted.append( + Item( + f"held-md-{n}", + "heldout_married_dob_typo", + dict( + person, + lname=MARRIED[(n + 2) % len(MARRIED)], + DOB=f"{y}-{m}-{int(d) % 28 + 1:02d}", + ), + None, + [], + ) + ) + out = list(items) + for p in planted: + out.insert(rng.randint(0, len(out)), p) + return out + + +def record_of(i: int, person: dict[str, str]) -> PatientRecord: + return PatientRecord( + str(i), person["fname"], person["lname"], person.get("DOB", "") + ) + + +def words(value: str) -> list[str]: + return re.findall(r"[0-9a-z]+", (value or "").casefold()) + + +def screen_stops(params: dict[str, str], table: list[PatientRecord]) -> bool: + """The no_duplicate_candidate rule on OpenEMR's possible-duplicate list.""" + + lname, dob = params.get("lname", ""), params.get("DOB", "") + if not words(lname) or not dob: + return True # a missing parameter fails the check closed + fname = params.get("fname", "") + listed = [ + r + for r in table + if words(r.fname) == words(fname) + or words(r.lname) == words(lname) + or r.dob == dob + ] + for r in listed: + row = set(words(f"{r.fname} {r.lname} {r.dob}")) + if all(w in row for w in words(lname)) or r.dob == dob: + return True + return False + + +def measure(existing: list[dict[str, str]], items: list[Item]) -> dict[str, Any]: + table = [record_of(i, p) for i, p in enumerate(existing)] + rows = [] + for item in items: + for person in item.before: + table.append(record_of(len(table), person)) + params = { + k: (v.strip() if isinstance(v, str) else v) for k, v in item.params.items() + } + params["DOB"] = iso(params.get("DOB", "")) + is_dup = item.exception in DUPLICATE_KINDS + new_chart = item.intended is not None + screen = screen_stops(params, table) + result = check_duplicate(TableSource(table), params) + rows.append( + { + "item": item.item_id, + "exception": item.exception, + "right": "new chart" + if new_chart + else ("no chart: duplicate" if is_dup else "no chart"), + "screen": "stop" if screen else "proceed", + "record": result.decision, + "record_why": [c.why for c in result.candidates], + } + ) + if item.intended is not None: + table.append(record_of(len(table), item.intended)) + return {"rows": rows, "summary": summarize(rows)} + + +def summarize(rows: list[dict[str, Any]]) -> dict[str, Any]: + out: dict[str, Any] = {"items": len(rows)} + new = [r for r in rows if r["right"] == "new chart"] + dups = [r for r in rows if r["right"] == "no chart: duplicate"] + out["new_chart_items"], out["true_duplicates"] = len(new), len(dups) + for check, stops in ( + ("screen", lambda r: r["screen"] == "stop"), + ("record", lambda r: r["record"] != "none"), + ): + false_stops = [r for r in new if stops(r)] + missed = [r for r in dups if not stops(r)] + out[check] = { + "false_stops": len(false_stops), + "false_stops_per_100_items": round(100 * len(false_stops) / len(rows), 1), + "false_stops_by_exception": _count(false_stops), + "missed_duplicates": len(missed), + "missed_by_exception": _count(missed), + "caught_duplicates": len(dups) - len(missed), + } + out["record"]["decisions"] = _count(rows, key="record") + return out + + +def _count(rows: list[dict[str, Any]], key: str = "exception") -> dict[str, int]: + counts: dict[str, int] = {} + for r in rows: + counts[r[key]] = counts.get(r[key], 0) + 1 + return dict(sorted(counts.items())) + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__.splitlines()[0]) + parser.add_argument("--out", type=Path, default=HERE / "results") + parser.add_argument("--long-items", type=int, default=500) + args = parser.parse_args() + args.out.mkdir(parents=True, exist_ok=True) + runs = {} + existing, items = from_ledger(SOAK / "workload.jsonl") + runs["soak-seed7-55"] = measure(existing, plant(existing, items, 7)) + existing, items = from_generator(7, args.long_items, 10) + runs[f"seed7-{args.long_items}"] = measure(existing, plant(existing, items, 7)) + for name, run in runs.items(): + (args.out / f"{name}.json").write_text( + json.dumps(run, indent=2, sort_keys=True) + "\n" + ) + print(name, json.dumps(run["summary"], sort_keys=True)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/packages/flow/benchmark/dup_check/results/seed7-500.json b/packages/flow/benchmark/dup_check/results/seed7-500.json new file mode 100644 index 000000000..af7f2cb31 --- /dev/null +++ b/packages/flow/benchmark/dup_check/results/seed7-500.json @@ -0,0 +1,4753 @@ +{ + "rows": [ + { + "exception": "clean", + "item": "ref-00001", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00002", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00003", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00004", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "shared_dob", + "item": "ref-00005", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "malformed_dob", + "item": "ref-00006", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00007", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "missing_required", + "item": "ref-00008", + "record": "unreadable", + "record_why": [], + "right": "no chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00009", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00010", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00011", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "concurrent_same", + "item": "ref-00012", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "concurrent_other", + "item": "ref-00013", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00014", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00015", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00016", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00017", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00018", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "malformed_dob", + "item": "ref-00019", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00020", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "dup_resend", + "item": "ref-00021", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00022", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00023", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "ambiguous_same_person", + "item": "ref-00024", + "record": "close", + "record_why": [ + "same name, date of birth one field off" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_married", + "item": "plant-m-1", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00025", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "malformed_case", + "item": "ref-00026", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "common_surname", + "item": "ref-00027", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00028", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "concurrent_same", + "item": "ref-00029", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "common_surname", + "item": "ref-00030", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00031", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "missing_optional", + "item": "ref-00032", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "common_surname", + "item": "ref-00033", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "dup_existing", + "item": "ref-00034", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00035", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00036", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-1", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-2", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00037", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "slow_save", + "item": "ref-00038", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00039", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "shared_dob", + "item": "ref-00040", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "shared_dob", + "item": "ref-00041", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00042", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00043", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "dup_resend", + "item": "ref-00044", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-5", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "dup_existing", + "item": "ref-00045", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "session_expiry", + "item": "ref-00046", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-1", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-4", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00047", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00048", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00049", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00050", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00051", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "dup_existing", + "item": "ref-00052", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "missing_optional", + "item": "ref-00053", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "common_surname", + "item": "ref-00054", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "missing_required", + "item": "ref-00055", + "record": "unreadable", + "record_why": [], + "right": "no chart", + "screen": "stop" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-7", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "shared_dob", + "item": "ref-00056", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "planted_married", + "item": "plant-m-4", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-2", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-7", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00057", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-4", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "failed_save", + "item": "ref-00058", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00059", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00060", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00061", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "shared_dob", + "item": "ref-00062", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "shared_dob", + "item": "ref-00063", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-11", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "dup_existing", + "item": "ref-00064", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00065", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00066", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "planted_swapped_dob", + "item": "plant-s-3", + "record": "close", + "record_why": [ + "same name, day and month swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "session_expiry", + "item": "ref-00067", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00068", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00069", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "dup_resend", + "item": "ref-00070", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00071", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "planted_swapped_dob", + "item": "plant-s-10", + "record": "close", + "record_why": [ + "same name, day and month swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00072", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "concurrent_other", + "item": "ref-00073", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00074", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00075", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "shared_dob", + "item": "ref-00076", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00077", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-0", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00078", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00079", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "common_surname", + "item": "ref-00080", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00081", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00082", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-2", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "ambiguous_same_person", + "item": "ref-00083", + "record": "close", + "record_why": [ + "same name, date of birth one field off" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00084", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-3", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00085", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "concurrent_same", + "item": "ref-00086", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-7", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00087", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "dup_resend", + "item": "ref-00088", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00089", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "shared_dob", + "item": "ref-00090", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00091", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "missing_required", + "item": "ref-00092", + "record": "unreadable", + "record_why": [], + "right": "no chart", + "screen": "stop" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-6", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00093", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "dup_existing", + "item": "ref-00094", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "shared_dob", + "item": "ref-00095", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00096", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "shared_dob", + "item": "ref-00097", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00098", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00099", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00100", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00101", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-11", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00102", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "missing_optional", + "item": "ref-00103", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "missing_optional", + "item": "ref-00104", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00105", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00106", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00107", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-6", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "shared_dob", + "item": "ref-00108", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "dup_resend", + "item": "ref-00109", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00110", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00111", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00112", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "dup_existing", + "item": "ref-00113", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "concurrent_other", + "item": "ref-00114", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "ambiguous_same_person", + "item": "ref-00115", + "record": "close", + "record_why": [ + "same name, date of birth one field off" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00116", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-4", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "malformed_dob", + "item": "ref-00117", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "common_surname", + "item": "ref-00118", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00119", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00120", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00121", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00122", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-5", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00123", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "dup_resend", + "item": "ref-00124", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00125", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00126", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00127", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "shared_dob", + "item": "ref-00128", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00129", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00130", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00131", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "shared_dob", + "item": "ref-00132", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "planted_married", + "item": "plant-m-6", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00133", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00134", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "session_expiry", + "item": "ref-00135", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00136", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00137", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00138", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00139", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "common_surname", + "item": "ref-00140", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "session_expiry", + "item": "ref-00141", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "slow_save", + "item": "ref-00142", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00143", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00144", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "slow_save", + "item": "ref-00145", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00146", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00147", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00148", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00149", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00150", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "shared_dob", + "item": "ref-00151", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "common_surname", + "item": "ref-00152", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00153", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00154", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00155", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00156", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "dup_resend", + "item": "ref-00157", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00158", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "shared_dob", + "item": "ref-00159", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00160", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00161", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "common_surname", + "item": "ref-00162", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00163", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "planted_married", + "item": "plant-m-10", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00164", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00165", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "planted_married", + "item": "plant-m-0", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00166", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00167", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-6", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00168", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "ambiguous_same_person", + "item": "ref-00169", + "record": "close", + "record_why": [ + "same name, date of birth one field off" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00170", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "dup_existing", + "item": "ref-00171", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00172", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "planted_married", + "item": "plant-m-7", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "dup_resend", + "item": "ref-00173", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "common_surname", + "item": "ref-00174", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00175", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00176", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "common_surname", + "item": "ref-00177", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "malformed_dob", + "item": "ref-00178", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00179", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00180", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00181", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "ambiguous_new_person", + "item": "ref-00182", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "planted_married", + "item": "plant-m-2", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "shared_dob", + "item": "ref-00183", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00184", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "ambiguous_new_person", + "item": "ref-00185", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "planted_married", + "item": "plant-m-8", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "dup_resend", + "item": "ref-00186", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00187", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "session_expiry", + "item": "ref-00188", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00189", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00190", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00191", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00192", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "ambiguous_same_person", + "item": "ref-00193", + "record": "close", + "record_why": [ + "same name, date of birth one field off" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "dup_resend", + "item": "ref-00194", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "common_surname", + "item": "ref-00195", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00196", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00197", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00198", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "common_surname", + "item": "ref-00199", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00200", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-0", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "concurrent_other", + "item": "ref-00201", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00202", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "failed_save", + "item": "ref-00203", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00204", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00205", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00206", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00207", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00208", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "missing_optional", + "item": "ref-00209", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00210", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "planted_married", + "item": "plant-m-5", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "shared_dob", + "item": "ref-00211", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-2", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00212", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00213", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "planted_swapped_dob", + "item": "plant-s-4", + "record": "close", + "record_why": [ + "same name, day and month swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00214", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00215", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00216", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "common_surname", + "item": "ref-00217", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00218", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-10", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "session_expiry", + "item": "ref-00219", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "common_surname", + "item": "ref-00220", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00221", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00222", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00223", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00224", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-9", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00225", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00226", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "shared_dob", + "item": "ref-00227", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "dup_existing", + "item": "ref-00228", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "shared_dob", + "item": "ref-00229", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00230", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00231", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00232", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "concurrent_same", + "item": "ref-00233", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00234", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-3", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "malformed_case", + "item": "ref-00235", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "missing_optional", + "item": "ref-00236", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00237", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "failed_save", + "item": "ref-00238", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "session_expiry", + "item": "ref-00239", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "malformed_dob", + "item": "ref-00240", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "slow_save", + "item": "ref-00241", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "malformed_case", + "item": "ref-00242", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00243", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "concurrent_same", + "item": "ref-00244", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "slow_save", + "item": "ref-00245", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "malformed_dob", + "item": "ref-00246", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "concurrent_other", + "item": "ref-00247", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "missing_optional", + "item": "ref-00248", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "shared_dob", + "item": "ref-00249", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00250", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "shared_dob", + "item": "ref-00251", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00252", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00253", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "common_surname", + "item": "ref-00254", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00255", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "missing_required", + "item": "ref-00256", + "record": "unreadable", + "record_why": [], + "right": "no chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00257", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00258", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "malformed_case", + "item": "ref-00259", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-11", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "concurrent_other", + "item": "ref-00260", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00261", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00262", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00263", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "malformed_case", + "item": "ref-00264", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00265", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00266", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "missing_optional", + "item": "ref-00267", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00268", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "missing_optional", + "item": "ref-00269", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-1", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00270", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00271", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-10", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00272", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00273", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-5", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-9", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "ambiguous_new_person", + "item": "ref-00274", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "ambiguous_new_person", + "item": "ref-00275", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00276", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00277", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00278", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00279", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "missing_optional", + "item": "ref-00280", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00281", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00282", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "missing_required", + "item": "ref-00283", + "record": "unreadable", + "record_why": [], + "right": "no chart", + "screen": "stop" + }, + { + "exception": "slow_save", + "item": "ref-00284", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "common_surname", + "item": "ref-00285", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "missing_required", + "item": "ref-00286", + "record": "unreadable", + "record_why": [], + "right": "no chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00287", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00288", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-8", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00289", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-6", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00290", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "common_surname", + "item": "ref-00291", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00292", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "dup_resend", + "item": "ref-00293", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00294", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00295", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "malformed_dob", + "item": "ref-00296", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "ambiguous_same_person", + "item": "ref-00297", + "record": "close", + "record_why": [ + "same name, date of birth one field off" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "shared_dob", + "item": "ref-00298", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "dup_existing", + "item": "ref-00299", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00300", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "ambiguous_same_person", + "item": "ref-00301", + "record": "close", + "record_why": [ + "same name, date of birth one field off" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "common_surname", + "item": "ref-00302", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00303", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00304", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00305", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00306", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00307", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "session_expiry", + "item": "ref-00308", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00309", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "concurrent_other", + "item": "ref-00310", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "planted_married", + "item": "plant-m-11", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00311", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00312", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "concurrent_other", + "item": "ref-00313", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "failed_save", + "item": "ref-00314", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00315", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00316", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "missing_optional", + "item": "ref-00317", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "shared_dob", + "item": "ref-00318", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00319", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00320", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "shared_dob", + "item": "ref-00321", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "failed_save", + "item": "ref-00322", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "dup_resend", + "item": "ref-00323", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00324", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00325", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00326", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00327", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00328", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "session_expiry", + "item": "ref-00329", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "malformed_case", + "item": "ref-00330", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-0", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "malformed_dob", + "item": "ref-00331", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "ambiguous_new_person", + "item": "ref-00332", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "missing_optional", + "item": "ref-00333", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00334", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-9", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "slow_save", + "item": "ref-00335", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00336", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "shared_dob", + "item": "ref-00337", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "shared_dob", + "item": "ref-00338", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00339", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "concurrent_other", + "item": "ref-00340", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00341", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00342", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00343", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "failed_save", + "item": "ref-00344", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "common_surname", + "item": "ref-00345", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00346", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00347", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00348", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "missing_optional", + "item": "ref-00349", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00350", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-7", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "ambiguous_new_person", + "item": "ref-00351", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00352", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00353", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "malformed_dob", + "item": "ref-00354", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00355", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "shared_dob", + "item": "ref-00356", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "common_surname", + "item": "ref-00357", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00358", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "missing_required", + "item": "ref-00359", + "record": "unreadable", + "record_why": [], + "right": "no chart", + "screen": "stop" + }, + { + "exception": "dup_resend", + "item": "ref-00360", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00361", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "malformed_dob", + "item": "ref-00362", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "slow_save", + "item": "ref-00363", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "concurrent_other", + "item": "ref-00364", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00365", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00366", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00367", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00368", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00369", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00370", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00371", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00372", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00373", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00374", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00375", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00376", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00377", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00378", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00379", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "shared_dob", + "item": "ref-00380", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00381", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00382", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00383", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "common_surname", + "item": "ref-00384", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00385", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "malformed_dob", + "item": "ref-00386", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-4", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00387", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00388", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00389", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00390", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "missing_required", + "item": "ref-00391", + "record": "unreadable", + "record_why": [], + "right": "no chart", + "screen": "stop" + }, + { + "exception": "concurrent_other", + "item": "ref-00392", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00393", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "missing_optional", + "item": "ref-00394", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00395", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "malformed_case", + "item": "ref-00396", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00397", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "shared_dob", + "item": "ref-00398", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00399", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "missing_optional", + "item": "ref-00400", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-8", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "common_surname", + "item": "ref-00401", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "dup_existing", + "item": "ref-00402", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "missing_required", + "item": "ref-00403", + "record": "unreadable", + "record_why": [], + "right": "no chart", + "screen": "stop" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-5", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00404", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "ambiguous_same_person", + "item": "ref-00405", + "record": "close", + "record_why": [ + "same name, date of birth one field off" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00406", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00407", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00408", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00409", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00410", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "missing_required", + "item": "ref-00411", + "record": "unreadable", + "record_why": [], + "right": "no chart", + "screen": "stop" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-11", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "missing_required", + "item": "ref-00412", + "record": "unreadable", + "record_why": [], + "right": "no chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00413", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00414", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-1", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-3", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00415", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00416", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-3", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "concurrent_other", + "item": "ref-00417", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00418", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00419", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00420", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "dup_resend", + "item": "ref-00421", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-9", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00422", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00423", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "dup_resend", + "item": "ref-00424", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00425", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "ambiguous_new_person", + "item": "ref-00426", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00427", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00428", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00429", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "dup_existing", + "item": "ref-00430", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_married", + "item": "plant-m-3", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "dup_existing", + "item": "ref-00431", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00432", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "planted_married", + "item": "plant-m-9", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "shared_dob", + "item": "ref-00433", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00434", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00435", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00436", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "ambiguous_same_person", + "item": "ref-00437", + "record": "close", + "record_why": [ + "same name, date of birth one field off" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00438", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00439", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00440", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00441", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00442", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "shared_dob", + "item": "ref-00443", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "missing_required", + "item": "ref-00444", + "record": "unreadable", + "record_why": [], + "right": "no chart", + "screen": "stop" + }, + { + "exception": "slow_save", + "item": "ref-00445", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00446", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00447", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00448", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "dup_resend", + "item": "ref-00449", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00450", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00451", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00452", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00453", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-10", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "session_expiry", + "item": "ref-00454", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "shared_dob", + "item": "ref-00455", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00456", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00457", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "malformed_case", + "item": "ref-00458", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "slow_save", + "item": "ref-00459", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00460", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "failed_save", + "item": "ref-00461", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "common_surname", + "item": "ref-00462", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00463", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00464", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00465", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00466", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-8", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00467", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "dup_resend", + "item": "ref-00468", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00469", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "missing_required", + "item": "ref-00470", + "record": "unreadable", + "record_why": [], + "right": "no chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00471", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00472", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "slow_save", + "item": "ref-00473", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00474", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00475", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "dup_resend", + "item": "ref-00476", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00477", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "ambiguous_same_person", + "item": "ref-00478", + "record": "close", + "record_why": [ + "same name, date of birth one field off" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "concurrent_same", + "item": "ref-00479", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "dup_resend", + "item": "ref-00480", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00481", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "common_surname", + "item": "ref-00482", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00483", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-10", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "common_surname", + "item": "ref-00484", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-0", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00485", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00486", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00487", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "common_surname", + "item": "ref-00488", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "shared_dob", + "item": "ref-00489", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00490", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00491", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "failed_save", + "item": "ref-00492", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00493", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00494", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00495", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00496", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00497", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00498", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00499", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00500", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-8", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + } + ], + "summary": { + "items": 563, + "new_chart_items": 440, + "record": { + "caught_duplicates": 98, + "decisions": { + "close": 63, + "exact": 37, + "none": 450, + "unreadable": 13 + }, + "false_stops": 2, + "false_stops_by_exception": { + "shared_dob": 2 + }, + "false_stops_per_100_items": 0.4, + "missed_by_exception": { + "heldout_married_dob_typo": 12 + }, + "missed_duplicates": 12 + }, + "screen": { + "caught_duplicates": 98, + "false_stops": 252, + "false_stops_by_exception": { + "ambiguous_new_person": 7, + "clean": 151, + "common_surname": 27, + "concurrent_other": 7, + "failed_save": 3, + "malformed_case": 6, + "malformed_dob": 2, + "missing_optional": 7, + "session_expiry": 4, + "shared_dob": 33, + "slow_save": 5 + }, + "false_stops_per_100_items": 44.8, + "missed_by_exception": { + "heldout_married_dob_typo": 12 + }, + "missed_duplicates": 12 + }, + "true_duplicates": 110 + } +} diff --git a/packages/flow/benchmark/dup_check/results/soak-seed7-55.json b/packages/flow/benchmark/dup_check/results/soak-seed7-55.json new file mode 100644 index 000000000..f442065c1 --- /dev/null +++ b/packages/flow/benchmark/dup_check/results/soak-seed7-55.json @@ -0,0 +1,1102 @@ +{ + "rows": [ + { + "exception": "clean", + "item": "ref-00001", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00002", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00003", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-0", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-2", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-6", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_married", + "item": "plant-m-1", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-11", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-6", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-10", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00004", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-5", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-4", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "shared_dob", + "item": "ref-00005", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "planted_swapped_dob", + "item": "plant-s-10", + "record": "close", + "record_why": [ + "same name, day and month swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_swapped_dob", + "item": "plant-s-3", + "record": "close", + "record_why": [ + "same name, day and month swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-9", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_married", + "item": "plant-m-2", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "malformed_dob", + "item": "ref-00006", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "planted_married", + "item": "plant-m-8", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-3", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00007", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "planted_married", + "item": "plant-m-7", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-7", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-2", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-4", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-11", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "missing_required", + "item": "ref-00008", + "record": "unreadable", + "record_why": [], + "right": "no chart", + "screen": "stop" + }, + { + "exception": "planted_married", + "item": "plant-m-10", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_married", + "item": "plant-m-9", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00009", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-0", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-1", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00010", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00011", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "concurrent_same", + "item": "ref-00012", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "concurrent_other", + "item": "ref-00013", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00014", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00015", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-6", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00016", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00017", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-4", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00018", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "malformed_dob", + "item": "ref-00019", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00020", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-8", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-7", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_married", + "item": "plant-m-0", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "dup_resend", + "item": "ref-00021", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-3", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-10", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-2", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00022", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00023", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-0", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "ambiguous_same_person", + "item": "ref-00024", + "record": "close", + "record_why": [ + "same name, date of birth one field off" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00025", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "malformed_case", + "item": "ref-00026", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "common_surname", + "item": "ref-00027", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00028", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "concurrent_same", + "item": "ref-00029", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-1", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-11", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "common_surname", + "item": "ref-00030", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-7", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00031", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "missing_optional", + "item": "ref-00032", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "planted_married", + "item": "plant-m-6", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "common_surname", + "item": "ref-00033", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "dup_existing", + "item": "ref-00034", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00035", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00036", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00037", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-10", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_married", + "item": "plant-m-11", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "slow_save", + "item": "ref-00038", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-0", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-1", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "planted_married", + "item": "plant-m-4", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00039", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-11", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "shared_dob", + "item": "ref-00040", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-9", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-8", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-9", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-7", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-3", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "shared_dob", + "item": "ref-00041", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-8", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00042", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-9", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "planted_married", + "item": "plant-m-3", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00043", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "dup_resend", + "item": "ref-00044", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-6", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "dup_existing", + "item": "ref-00045", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-10", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "session_expiry", + "item": "ref-00046", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-1", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00047", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00048", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00049", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-8", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-5", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-5", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00050", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00051", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-5", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_swapped_dob", + "item": "plant-s-4", + "record": "close", + "record_why": [ + "same name, day and month swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-3", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "dup_existing", + "item": "ref-00052", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "missing_optional", + "item": "ref-00053", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-2", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "common_surname", + "item": "ref-00054", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "planted_married", + "item": "plant-m-5", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-4", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "missing_required", + "item": "ref-00055", + "record": "unreadable", + "record_why": [], + "right": "no chart", + "screen": "stop" + } + ], + "summary": { + "items": 118, + "new_chart_items": 45, + "record": { + "caught_duplicates": 59, + "decisions": { + "close": 52, + "exact": 7, + "none": 57, + "unreadable": 2 + }, + "false_stops": 0, + "false_stops_by_exception": {}, + "false_stops_per_100_items": 0.0, + "missed_by_exception": { + "heldout_married_dob_typo": 12 + }, + "missed_duplicates": 12 + }, + "screen": { + "caught_duplicates": 59, + "false_stops": 10, + "false_stops_by_exception": { + "clean": 2, + "common_surname": 4, + "shared_dob": 3, + "slow_save": 1 + }, + "false_stops_per_100_items": 8.5, + "missed_by_exception": { + "heldout_married_dob_typo": 12 + }, + "missed_duplicates": 12 + }, + "true_duplicates": 71 + } +} diff --git a/packages/flow/docs/DUPLICATE_CHECK.md b/packages/flow/docs/DUPLICATE_CHECK.md index 02fffc8ae..2b88b79e3 100644 --- a/packages/flow/docs/DUPLICATE_CHECK.md +++ b/packages/flow/docs/DUPLICATE_CHECK.md @@ -61,3 +61,29 @@ on the OpenEMR referral program, and the soak admits that version afresh `04/20/1944`, ISO), and a row date that reads either way counts when either reading matches. Other values are matched word for word, so name only parameters the list shows as they were typed. + +## Asking the system of record instead + +When the deployment's `effects` reads a FHIR server or OpenEMR's patient API, +`no_duplicate_candidate` searches the system of record for the patient instead +of reading the on-screen list (`runtime/checks/duplicate_record.py`, a check +whose source is the record). `runtime.duplicate_check: screen` keeps the +on-screen list. With no such channel, the on-screen list decides, as above. A +read-only SQL query on `patient_data` (`SqlPatientSource`) is the documented +fallback for a deployment with neither; it is wired by hand, not by default. + +The check searches by the date of birth, by the date of birth with day and +month swapped, by each part of the last name, and by MRN when the run has one. +It reads every page, and it decides from whole records: + +| Decision | When | What happens | +|---|---|---| +| `exact` | same first name, last name and date of birth, or the same MRN | stop: the chart exists | +| `close` | same name with the date of birth one field off or day and month swapped; same date of birth with a similar name or a shared hyphenated part; same date of birth and first name under another last name | stop for a person; the halt names each record and why | +| `none` | no record is this patient | proceed | +| `unreadable` | unreachable, refused credentials, not a result set, a reported total that doesn't match what was read, more than 50 pages, a full REST page with no next link, a missing or non-ISO date of birth | stop (fail closed) | + +Sharing only a last name, or only a date of birth, doesn't stop the item. + +Measured offline on the production soak's seed-7 stream, with true +duplicates planted: `benchmark/dup_check/README.md`. diff --git a/packages/flow/openadapt_flow/deployment.py b/packages/flow/openadapt_flow/deployment.py index 3fa342ef1..7a102f2f9 100644 --- a/packages/flow/openadapt_flow/deployment.py +++ b/packages/flow/openadapt_flow/deployment.py @@ -761,6 +761,13 @@ class RuntimeSection(BaseModel): #: :func:`openadapt_flow.runtime.identity.verify_pixel_identity` and #: docs/LIMITS.md. pixel_verify_enabled: bool = False + #: Where ``no_duplicate_candidate`` gets its evidence. ``record`` (the + #: default): when ``effects`` reads a FHIR server or OpenEMR's patient API, + #: the check searches the system of record for this patient + #: (``runtime/checks/duplicate_record.py``) and the app's on-screen match + #: list is not read. With no such channel, or ``screen``, the on-screen + #: list decides, as before. + duplicate_check: Literal["record", "screen"] = "record" #: Path to an export of the system of record that lists every identifier #: in the namespace (CSV, JSON or one per line). When set, an OCR identity #: check that abstained only on a look-alike identifier (O/0, l/1, B/8) is @@ -1440,6 +1447,16 @@ def build_model_grounder(cfg: GroundingModelConfig) -> Optional[Any]: return None +def _duplicate_record_source(effect_verifier: Any) -> Any: + """The duplicate check's record channel, from the deployment's verifier.""" + + if effect_verifier is None: + return None + from openadapt_flow.runtime.checks.duplicate_record import source_for_verifier + + return source_for_verifier(effect_verifier) + + def build_replayer( backend: Any, *, @@ -1650,6 +1667,11 @@ def build_replayer( state_verifier=(appliance.state_verifier if appliance else None), allow_model_grounding=allow_egress, effect_verifier=effect_verifier, + duplicate_record_source=( + None + if runtime_config is not None and runtime_config.duplicate_check == "screen" + else _duplicate_record_source(effect_verifier) + ), api_actuator=api_actuator, durable=durable, use_structural=use_structural, diff --git a/packages/flow/openadapt_flow/runtime/checks/__init__.py b/packages/flow/openadapt_flow/runtime/checks/__init__.py new file mode 100644 index 000000000..c8610527a --- /dev/null +++ b/packages/flow/openadapt_flow/runtime/checks/__init__.py @@ -0,0 +1,14 @@ +"""Checks tagged with where their evidence comes from. + +``docs/design/DECOMPOSITION.md`` S5: a check reads either the screen or the +system of record. This package holds checks whose source is the record. +""" + +from __future__ import annotations + +from typing import Literal + +#: Where a check's evidence comes from. +CheckSource = Literal["screen", "record"] + +__all__ = ["CheckSource"] diff --git a/packages/flow/openadapt_flow/runtime/checks/duplicate_record.py b/packages/flow/openadapt_flow/runtime/checks/duplicate_record.py new file mode 100644 index 000000000..d8e8c95f1 --- /dev/null +++ b/packages/flow/openadapt_flow/runtime/checks/duplicate_record.py @@ -0,0 +1,626 @@ +"""Is this new patient already in the system of record? Asked of the record. + +The screen check (``no_duplicate_candidate``) reads the app's possible- +duplicate list and stops on any row that shares the last name or the date of +birth. Every new patient who shares either with an existing chart stops for a +person, and that is most of the operator time in the production soak. + +This check asks the system of record instead (FHIR ``Patient`` search, the +app's REST API, or, as a documented fallback, a read-only SQL query) and +decides from whole records: + +- ``exact``: same first name, last name and date of birth, or the same MRN. + Stop: the chart exists. +- ``close``: a record that may be this person. Stop for a person, with both + records shown. Close is any of: + - the same name with a date of birth one field off (a typo) or with day and + month swapped; + - the same date of birth with a similar first name and a similar or + hyphen-sharing last name; + - the same date of birth and the same first name, whatever the last name + (a married or changed name). +- ``none``: no record is this person. Proceed. +- ``unreadable``: the channel was unreachable, refused the credentials, + returned something that is not a result set, or could not be read to the + last page. Stop (fail closed). + +Sharing only a last name, or only a date of birth, is not a candidate: those +are the ordinary coincidences a practice sees. +""" + +from __future__ import annotations + +import re +import unicodedata +from dataclasses import dataclass, field +from typing import Any, Callable, Iterable, Literal, Mapping, Optional, Protocol +from urllib.parse import urljoin + +from openadapt_flow.runtime.checks import CheckSource + +Decision = Literal["none", "exact", "close", "unreadable"] + +#: A paging chain longer than this is read as unreadable, not truncated. +MAX_PAGES = 50 + + +@dataclass(frozen=True) +class PatientRecord: + """One chart as the record channel returns it.""" + + record_id: str + fname: str + lname: str + dob: str # ISO YYYY-MM-DD, or "" when the record has none + mrn: str = "" + + +@dataclass +class Candidate: + record: PatientRecord + match: Literal["exact", "close"] + why: str + + +@dataclass +class DuplicateCheckResult: + decision: Decision + source: CheckSource = "record" + channel: str = "" + reason: str = "" + candidates: list[Candidate] = field(default_factory=list) + #: Search requests made and pages read, for the receipt. + searches: int = 0 + pages: int = 0 + + @property + def proceed(self) -> bool: + return self.decision == "none" + + +class Unreadable(Exception): + """The record channel could not give a complete answer.""" + + +class PatientSource(Protocol): + """A read-only patient search on the system of record.""" + + channel: str + + def by_birthdate(self, dob: str) -> list[PatientRecord]: ... + + def by_family(self, family: str) -> list[PatientRecord]: ... + + def by_identifier(self, mrn: str) -> list[PatientRecord]: ... + + +# -- matching ------------------------------------------------------------------- + + +def norm_name(value: str) -> str: + """Case, accents, spaces and punctuation removed (hyphens kept).""" + + text = unicodedata.normalize("NFKD", value or "") + text = "".join(c for c in text if not unicodedata.combining(c)).casefold() + text = re.sub(r"[^a-z\- ]", "", text) + return re.sub(r"\s+", " ", text).strip() + + +def name_parts(value: str) -> set[str]: + return {p for p in re.split(r"[\s\-]+", norm_name(value)) if p} + + +def edit_distance(a: str, b: str) -> int: + """Damerau-Levenshtein (optimal string alignment).""" + + d = [[0] * (len(b) + 1) for _ in range(len(a) + 1)] + for i in range(len(a) + 1): + d[i][0] = i + for j in range(len(b) + 1): + d[0][j] = j + for i in range(1, len(a) + 1): + for j in range(1, len(b) + 1): + cost = 0 if a[i - 1] == b[j - 1] else 1 + d[i][j] = min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost) + if i > 1 and j > 1 and a[i - 1] == b[j - 2] and a[i - 2] == b[j - 1]: + d[i][j] = min(d[i][j], d[i - 2][j - 2] + 1) + return d[len(a)][len(b)] + + +def soundex(value: str) -> str: + letters = re.sub(r"[^a-z]", "", norm_name(value)) + if not letters: + return "" + codes = { + c: str(n) + for n, group in enumerate(("bfpv", "cgjkqsxz", "dt", "l", "mn", "r"), start=1) + for c in group + } + out, last = letters[0].upper(), codes.get(letters[0], "") + for c in letters[1:]: + code = codes.get(c, "") + if code and code != last: + out += code + if c not in "hw": + last = code + return (out + "000")[:4] + + +def similar_name(a: str, b: str) -> bool: + """Equal, one or two edits apart (by length), or the same Soundex code.""" + + x, y = norm_name(a).replace("-", " "), norm_name(b).replace("-", " ") + if not x or not y: + return False + if x == y: + return True + limit = 1 if min(len(x), len(y)) <= 5 else 2 + if edit_distance(x, y) <= limit: + return True + return len(x) > 3 and len(y) > 3 and soundex(x) == soundex(y) + + +def swapped(dob: str) -> Optional[str]: + """The date with day and month swapped, when that is another valid date.""" + + y, m, d = dob.split("-") + if m == d or int(d) > 12: + return None + return f"{y}-{d}-{m}" + + +def one_field_off(a: str, b: str) -> bool: + """Two ISO dates that differ in exactly one of year, month and day.""" + + pa, pb = a.split("-"), b.split("-") + return sum(x != y for x, y in zip(pa, pb)) == 1 + + +def classify(want: Mapping[str, str], record: PatientRecord) -> Optional[Candidate]: + """Is ``record`` this new patient (``want``: fname, lname, DOB, mrn)?""" + + mrn = (want.get("mrn") or "").strip() + if mrn and record.mrn and mrn == record.mrn.strip(): + return Candidate(record, "exact", "same MRN") + fname, lname, dob = want["fname"], want["lname"], want["DOB"] + same_first = norm_name(fname) == norm_name(record.fname) + same_last = norm_name(lname) == norm_name(record.lname) + if same_first and same_last and dob == record.dob: + return Candidate( + record, "exact", "same first name, last name and date of birth" + ) + first_like = similar_name(fname, record.fname) + last_like = similar_name(lname, record.lname) or bool( + name_parts(lname) & name_parts(record.lname) + ) + if first_like and last_like: + if not record.dob: + return Candidate( + record, "close", "same name; the record has no date of birth" + ) + if record.dob == dob: + return Candidate(record, "close", "same date of birth, a similar name") + if record.dob == swapped(dob): + return Candidate(record, "close", "same name, day and month swapped") + if same_first and same_last and one_field_off(dob, record.dob): + return Candidate(record, "close", "same name, date of birth one field off") + if record.dob == dob and same_first: + return Candidate( + record, "close", "same date of birth and first name, another last name" + ) + return None + + +ISO_DATE = re.compile(r"\d{4}-\d{2}-\d{2}") + + +def check_duplicate( + source: PatientSource, want: Mapping[str, str] +) -> DuplicateCheckResult: + """Ask ``source`` whether the patient in ``want`` already has a chart.""" + + result = DuplicateCheckResult("unreadable", channel=source.channel) + fname, lname = (want.get("fname") or "").strip(), (want.get("lname") or "").strip() + dob = (want.get("DOB") or "").strip() + if not fname or not lname or not ISO_DATE.fullmatch(dob): + result.reason = ( + "the run's first name, last name or ISO date of birth is missing" + ) + return result + queries: list[tuple[Callable[[str], list[PatientRecord]], str]] = [ + (source.by_birthdate, dob) + ] + if swapped(dob): + queries.append((source.by_birthdate, swapped(dob))) # type: ignore[arg-type] + queries += [(source.by_family, part) for part in sorted(name_parts(lname))] + if (want.get("mrn") or "").strip(): + queries.append((source.by_identifier, want["mrn"].strip())) + seen: dict[str, PatientRecord] = {} + try: + for query, value in queries: + for record in query(value): + seen.setdefault(record.record_id, record) + result.searches += 1 + except Unreadable as exc: + result.reason = str(exc) + return result + result.pages = getattr(source, "pages_read", 0) + found = [c for c in (classify(want, r) for r in seen.values()) if c is not None] + result.candidates = sorted( + found, key=lambda c: (c.match != "exact", c.record.record_id) + ) + if any(c.match == "exact" for c in found): + result.decision = "exact" + result.reason = "a chart for this patient exists" + elif found: + result.decision = "close" + result.reason = "a chart may be this patient; a person decides" + else: + result.decision = "none" + result.reason = f"no chart matches among {len(seen)} record(s) read" + return result + + +# -- channels ------------------------------------------------------------------- + + +def _get_json( + session: Any, url: str, params: Optional[dict], headers: dict, timeout_s: float +) -> tuple[Any, str]: + try: + resp = session.get(url, params=params, headers=headers, timeout=timeout_s) + except Exception as exc: # noqa: BLE001 - transport failure is unreadable + raise Unreadable(f"the record channel is unreachable ({type(exc).__name__})") + if resp.status_code in (401, 403): + raise Unreadable( + f"the record channel refused the credentials (HTTP {resp.status_code})" + ) + if resp.status_code // 100 != 2: + raise Unreadable(f"the record channel returned HTTP {resp.status_code}") + try: + return resp.json(), str(getattr(resp, "url", url)) + except Exception: # noqa: BLE001 - unparseable body is unreadable + raise Unreadable("the record channel returned a body that is not JSON") + + +class FhirPatientSource: + """FHIR R4 ``Patient`` search, every page (``Bundle.link[next]``). + + A page chain longer than :data:`MAX_PAGES`, a ``Bundle.total`` that does + not equal the entries read, or an entry that is not a ``Patient`` makes + the answer unreadable. + """ + + channel = "fhir" + + def __init__( + self, + base_url: str, + *, + token: Optional[Callable[[], Optional[str]]] = None, + session: Any = None, + timeout_s: float = 10.0, + verify_tls: bool = True, + mrn_system: Optional[str] = None, + ) -> None: + self.base_url = base_url.rstrip("/") + self._token = token + self._session = session + self.timeout_s = timeout_s + self.verify_tls = verify_tls + self.mrn_system = mrn_system + self.pages_read = 0 + + def _client(self) -> Any: + if self._session is None: + import httpx + + self._session = httpx.Client(verify=self.verify_tls) + return self._session + + def _headers(self) -> dict[str, str]: + headers = {"Accept": "application/fhir+json"} + if self._token is not None: + token = self._token() + if not token: + raise Unreadable("no access token for the record channel") + headers["Authorization"] = f"Bearer {token}" + return headers + + def search(self, params: dict[str, str]) -> list[PatientRecord]: + url: Optional[str] = f"{self.base_url}/Patient" + query: Optional[dict[str, str]] = params + total: Optional[int] = None + out: list[PatientRecord] = [] + pages = 0 + while url is not None: + if pages >= MAX_PAGES: + raise Unreadable( + f"more than {MAX_PAGES} pages; the search was not read to the end" + ) + body, at = _get_json( + self._client(), url, query, self._headers(), self.timeout_s + ) + pages += 1 + self.pages_read += 1 + if not isinstance(body, dict) or body.get("resourceType") != "Bundle": + raise Unreadable("the record channel did not return a FHIR Bundle") + if total is None and isinstance(body.get("total"), int): + total = body["total"] + entries = body.get("entry", []) + if not isinstance(entries, list): + raise Unreadable("the Bundle's entries are not a list") + for entry in entries: + resource = entry.get("resource") if isinstance(entry, dict) else None + if ( + not isinstance(resource, dict) + or resource.get("resourceType") != "Patient" + ): + raise Unreadable("a Bundle entry is not a Patient") + out.append(self._record(resource)) + url, query = None, None + for link in body.get("link", []) or []: + if isinstance(link, dict) and link.get("relation") == "next": + raw = link.get("url") + if not isinstance(raw, str) or not raw.strip(): + raise Unreadable("a next-page link has no URL") + url = urljoin(at, raw.strip()) + if total is not None and total != len(out): + raise Unreadable( + f"the search reported {total} record(s) and {len(out)} were read" + ) + return out + + def _record(self, resource: dict[str, Any]) -> PatientRecord: + names = resource.get("name") or [] + official = next( + (n for n in names if isinstance(n, dict) and n.get("use") == "official"), + names[0] if names and isinstance(names[0], dict) else {}, + ) + given = official.get("given") or [] + mrn = "" + for ident in resource.get("identifier") or []: + if not isinstance(ident, dict): + continue + codes = [ + c.get("code") for c in (ident.get("type") or {}).get("coding", []) or [] + ] + if (self.mrn_system and ident.get("system") == self.mrn_system) or ( + not self.mrn_system and ("MR" in codes or "PT" in codes) + ): + mrn = str(ident.get("value") or "") + break + return PatientRecord( + record_id=str(resource.get("id") or ""), + fname=str(given[0]) if given else "", + lname=str(official.get("family") or ""), + dob=str(resource.get("birthDate") or ""), + mrn=mrn, + ) + + def by_birthdate(self, dob: str) -> list[PatientRecord]: + return self.search({"birthdate": dob}) + + def by_family(self, family: str) -> list[PatientRecord]: + return self.search({"family": family}) + + def by_identifier(self, mrn: str) -> list[PatientRecord]: + return self.search({"identifier": mrn}) + + +class SqlPatientSource: + """Fallback: a read-only query on OpenEMR's ``patient_data``. + + For a deployment with no FHIR or REST read. ``connect`` returns a DB-API + connection with a SELECT-only account. Uses ``qmark`` parameters. + """ + + channel = "sql" + + def __init__( + self, connect: Callable[[], Any], *, table: str = "patient_data" + ) -> None: + if not re.fullmatch(r"[A-Za-z_][A-Za-z0-9_.]{0,63}", table): + raise ValueError("table must be a plain identifier") + self._connect = connect + self.table = table + self.pages_read = 0 + + def _query(self, where: str, value: str) -> list[PatientRecord]: + try: + conn = self._connect() + except Exception as exc: # noqa: BLE001 + raise Unreadable(f"the database is unreachable ({type(exc).__name__})") + try: + cur = conn.cursor() + cur.execute( + f"SELECT pid, fname, lname, DOB, pubpid FROM {self.table} WHERE {where}", + (value,), + ) + rows = cur.fetchall() + except Exception as exc: # noqa: BLE001 + raise Unreadable(f"the database query failed ({type(exc).__name__})") + finally: + try: + conn.close() + except Exception: # noqa: BLE001 + pass + self.pages_read += 1 + return [ + PatientRecord( + str(pid), + str(f or ""), + str(lname or ""), + str(d or "")[:10], + str(mrn or ""), + ) + for pid, f, lname, d, mrn in rows + ] + + def by_birthdate(self, dob: str) -> list[PatientRecord]: + return self._query("DOB = ?", dob) + + def by_family(self, family: str) -> list[PatientRecord]: + # FHIR's string search is a case-insensitive prefix; match it. + return self._query("LOWER(lname) LIKE LOWER(?) || '%'", family) + + def by_identifier(self, mrn: str) -> list[PatientRecord]: + return self._query("pubpid = ?", mrn) + + +class TableSource: + """An in-memory patient table with FHIR search semantics (offline use).""" + + channel = "table" + + def __init__(self, records: Iterable[PatientRecord]) -> None: + self.records = list(records) + self.pages_read = 0 + + def by_birthdate(self, dob: str) -> list[PatientRecord]: + self.pages_read += 1 + return [r for r in self.records if r.dob == dob] + + def by_family(self, family: str) -> list[PatientRecord]: + self.pages_read += 1 + prefix = norm_name(family) + return [r for r in self.records if norm_name(r.lname).startswith(prefix)] + + def by_identifier(self, mrn: str) -> list[PatientRecord]: + self.pages_read += 1 + return [r for r in self.records if r.mrn and r.mrn == mrn] + + +class RestPatientSource: + """OpenEMR's standard REST API: ``GET .../api/patient?DOB=...``. + + OpenEMR answers with ``{"data": [...]}``. A next-page link + (``links.next``) is followed. A full page with no next link may be + truncated, so it makes the answer unreadable rather than incomplete. + """ + + channel = "rest" + + def __init__( + self, + base_url: str, + *, + path: str = "/apis/default/api/patient", + headers: Optional[dict[str, str]] = None, + session: Any = None, + timeout_s: float = 10.0, + page_size: int = 500, + ) -> None: + self.base_url = base_url.rstrip("/") + self.path = path.split("?", 1)[0] + self.headers = dict(headers or {}) + self._session = session + self.timeout_s = timeout_s + self.page_size = page_size + self.pages_read = 0 + + def _client(self) -> Any: + if self._session is None: + import httpx + + self._session = httpx.Client() + return self._session + + def search(self, params: dict[str, str]) -> list[PatientRecord]: + url: Optional[str] = f"{self.base_url}{self.path}" + query: Optional[dict[str, Any]] = {**params, "_limit": self.page_size} + out: list[PatientRecord] = [] + pages = 0 + while url is not None: + if pages >= MAX_PAGES: + raise Unreadable( + f"more than {MAX_PAGES} pages; the search was not read to the end" + ) + body, at = _get_json( + self._client(), url, query, self.headers, self.timeout_s + ) + pages += 1 + self.pages_read += 1 + rows = body.get("data") if isinstance(body, dict) else None + if ( + rows is None + and isinstance(body, dict) + and not body.get("validationErrors") + ): + rows = [] # OpenEMR returns no "data" for an empty search + if not isinstance(rows, list): + raise Unreadable("the patient search did not return a list") + for row in rows: + if not isinstance(row, dict): + raise Unreadable("a patient row is not an object") + out.append( + PatientRecord( + record_id=str( + row.get("uuid") or row.get("pid") or row.get("id") or "" + ), + fname=str(row.get("fname") or ""), + lname=str(row.get("lname") or ""), + dob=str(row.get("DOB") or "")[:10], + mrn=str(row.get("pubpid") or ""), + ) + ) + links = body.get("links") if isinstance(body, dict) else None + nxt = links.get("next") if isinstance(links, dict) else None + if isinstance(nxt, str) and nxt.strip(): + url, query = urljoin(at, nxt.strip()), None + elif len(rows) >= self.page_size: + raise Unreadable( + "a full page with no next link; the search may be truncated" + ) + else: + url = None + return out + + def by_birthdate(self, dob: str) -> list[PatientRecord]: + return self.search({"DOB": dob}) + + def by_family(self, family: str) -> list[PatientRecord]: + # OpenEMR matches lname exactly here, so compare prefixes locally + # against an exact read: a hyphenated or married name is still found + # through the date of birth. + return self.search({"lname": family}) + + def by_identifier(self, mrn: str) -> list[PatientRecord]: + return self.search({"pubpid": mrn}) + + +def source_for_verifier(verifier: Any) -> Optional[PatientSource]: + """The patient search on the same channel as a deployment's record check. + + A FHIR verifier gives a FHIR ``Patient`` search at its base URL with its + credentials; an OpenEMR REST verifier on ``.../api/patient`` gives the + REST search. Anything else gives None, and the screen check stays. + """ + + from openadapt_flow.runtime.effects import FhirEffectVerifier + from openadapt_flow.runtime.effects.rest import RestRecordVerifier + + if isinstance(verifier, FhirEffectVerifier): + + def token() -> Optional[str]: + if verifier.token_source is not None: + return verifier.token_source.access_token() + return verifier.access_token + + return FhirPatientSource( + verifier.base_url, + token=token if (verifier.access_token or verifier.token_source) else None, + verify_tls=verifier.verify_tls, + timeout_s=max(verifier.timeout_s, 10.0), + ) + if isinstance(verifier, RestRecordVerifier) and "/api/patient" in ( + verifier.records_path or "" + ): + return RestPatientSource( + verifier.base_url, + path=verifier.records_path, + headers=verifier.headers, + session=verifier._get_session(), + timeout_s=max(verifier.timeout_s, 10.0), + ) + return None diff --git a/packages/flow/openadapt_flow/runtime/replayer.py b/packages/flow/openadapt_flow/runtime/replayer.py index a841389f8..a9b4da3aa 100644 --- a/packages/flow/openadapt_flow/runtime/replayer.py +++ b/packages/flow/openadapt_flow/runtime/replayer.py @@ -868,6 +868,7 @@ def __init__( state_verifier: Optional[Any] = None, effect_verifier: Optional[EffectVerifier] = None, effect_compensator: Optional[Any] = None, + duplicate_record_source: Optional[Any] = None, api_actuator: Optional[Any] = None, web_form_actuator: Optional[Any] = None, api_steps: bool = True, @@ -1020,6 +1021,11 @@ def __init__( # makes a model call — the $0 runtime guarantee is preserved. self.effect_verifier = effect_verifier self.effect_compensator = effect_compensator + # The duplicate check's record channel (``runtime/checks/ + # duplicate_record.py``). When set, ``no_duplicate_candidate`` asks the + # system of record instead of reading the app's match list. + self.duplicate_record_source = duplicate_record_source + self._last_duplicate_check: Optional[Any] = None # ``run``-only, bundle-bound admission capability. The ordinary # ``replay`` path leaves this unset and preserves its permissive # behavior. When present it carries exact identity/effect decisions @@ -17732,6 +17738,9 @@ def _no_duplicate_candidate(self, pc: Any, frame_png: bytes) -> bool: or getattr(self, "_run_params", None) or {} ) + source = getattr(self, "duplicate_record_source", None) + if source is not None: + return self._no_duplicate_record(source, params) names = tuple(pc.candidate_params or ()) if not pc.candidate_rows or not names or not pc.text: return False @@ -17769,6 +17778,36 @@ def words(value: str) -> list[str]: return False return True + def _no_duplicate_record(self, source: Any, params: dict[str, Any]) -> bool: + """The duplicate check with the system of record as its source. + + Proceeds only when the record channel answers completely and no chart + is this patient (``decision == "none"``). An exact or close match, or + an unreadable channel, fails the check, and the halt names the + records found and why (``_describe_postcondition``). + """ + + from openadapt_flow.runtime.checks.duplicate_record import check_duplicate + + want = { + name: runtime_param_text(params[name]) if name in params else "" + for name in ("fname", "lname", "DOB", "mrn") + } + try: + result = check_duplicate(source, want) + except Exception as exc: # noqa: BLE001 - any failure fails closed + from openadapt_flow.runtime.checks.duplicate_record import ( + DuplicateCheckResult, + ) + + result = DuplicateCheckResult( + "unreadable", + channel=getattr(source, "channel", ""), + reason=f"the record check failed ({type(exc).__name__})", + ) + self._last_duplicate_check = result + return result.proceed + def _field_value_shown(self, pc: Any) -> bool: """The focused field shows this run's value of ``pc.param``. @@ -17800,10 +17839,21 @@ def squash(value: str) -> str: value = read("focused_text_value") return value is not None and value == expected - @staticmethod - def _describe_postcondition(pc: Any) -> str: + def _describe_postcondition(self, pc: Any) -> str: """Human-readable one-liner for a postcondition (for error messages).""" kind = pc.kind.value if hasattr(pc.kind, "value") else pc.kind + checked = getattr(self, "_last_duplicate_check", None) + if kind == "no_duplicate_candidate" and checked is not None: + found = "; ".join( + f"{c.match} record {c.record.record_id} ({c.why})" + for c in checked.candidates + ) + return ( + f"no_duplicate_candidate (record check via {checked.channel}: " + f"{checked.decision}, {checked.reason}" + + (f": {found}" if found else "") + + ")" + ) if kind == "local_edit": return "local_edit (URL, title, page count and focus unchanged)" if kind == "field_value": diff --git a/packages/flow/tests/test_duplicate_record_check.py b/packages/flow/tests/test_duplicate_record_check.py new file mode 100644 index 000000000..28575c849 --- /dev/null +++ b/packages/flow/tests/test_duplicate_record_check.py @@ -0,0 +1,296 @@ +"""The duplicate check with the system of record as its source. + +The screen check stops on any listed chart that shares the last name or the +date of birth. This check asks the record channel for the patient and stops +only on a chart that is, or may be, this patient, and on any channel failure. +The FHIR tests run against a real local HTTP server serving OpenEMR-shaped +``Patient`` bundles, page by page. +""" + +from __future__ import annotations + +import json +import sqlite3 +import threading +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +from pathlib import Path +from typing import Any, Callable, Optional +from urllib.parse import parse_qs, urlsplit + +import pytest + +from openadapt_flow.runtime.checks.duplicate_record import ( + FhirPatientSource, + PatientRecord, + RestPatientSource, + SqlPatientSource, + TableSource, + check_duplicate, + classify, +) + +WANT = {"fname": "Rosalind", "lname": "Jesswood", "DOB": "1990-04-09"} + + +def rec( + fname: str, lname: str, dob: str, rid: str = "1", mrn: str = "" +) -> PatientRecord: + return PatientRecord(rid, fname, lname, dob, mrn) + + +@pytest.mark.parametrize( + ("record", "match"), + [ + (rec("Rosalind", "Jesswood", "1990-04-09"), "exact"), + (rec(" ROSALIND ", "jesswood", "1990-04-09"), "exact"), + (rec("Ana", "Other", "1950-01-01", mrn="P77"), "exact"), # same MRN + (rec("Rosalind", "Okafor", "1990-04-09"), "close"), # married name + (rec("Rosalind", "Jesswood-Okafor", "1990-04-09"), "close"), # hyphenated + (rec("Rosalind", "Jesswood", "1990-09-04"), "close"), # day and month swapped + (rec("Rosalind", "Jesswood", "1990-04-10"), "close"), # a typo in the day + (rec("Rosalnid", "Jesswood", "1990-04-09"), "close"), # a typo in the name + (rec("Rosalind", "Jesswood", ""), "close"), # the record has no birth date + (rec("Ana", "Jesswood", "1961-01-01"), None), # a namesake + (rec("Ana", "Oakwood", "1990-04-09"), None), # a shared birth date + (rec("Rosalind", "Jesswood", "1971-11-23"), None), # same name, another person + ], +) +def test_classify(record: PatientRecord, match: Optional[str]) -> None: + found = classify({**WANT, "mrn": "P77"}, record) + assert (found.match if found else None) == match + + +def test_decisions_and_fail_closed_inputs() -> None: + table = TableSource( + [ + rec("Ana", "Jesswood", "1961-01-01", "1"), + rec("Ana", "Oakwood", "1990-04-09", "2"), + ] + ) + assert check_duplicate(table, WANT).decision == "none" + table.records.append(rec("Rosalind", "Jesswood", "1990-04-09", "3")) + result = check_duplicate(table, WANT) + assert result.decision == "exact" and result.source == "record" + assert [c.record.record_id for c in result.candidates] == ["3"] + # Not an ISO date (intake converts first), or a missing name: stop. + assert ( + check_duplicate(table, {**WANT, "DOB": "04/09/1990"}).decision == "unreadable" + ) + assert check_duplicate(table, {**WANT, "lname": " "}).decision == "unreadable" + + +# -- FHIR over HTTP --------------------------------------------------------------- + + +def patient(pid: str, given: str, family: str, dob: str) -> dict[str, Any]: + """Shaped like OpenEMR 8's FHIR Patient (official name, PT identifier).""" + + return { + "resourceType": "Patient", + "id": pid, + "name": [{"use": "official", "family": family, "given": [given]}], + "birthDate": dob, + "identifier": [{"type": {"coding": [{"code": "PT"}]}, "value": f"P{pid}"}], + } + + +class Server: + def __init__( + self, handle: Callable[[str, dict[str, list[str]]], tuple[int, Any]] + ) -> None: + outer = self + self.requests: list[str] = [] + + class H(BaseHTTPRequestHandler): + def do_GET(self) -> None: # noqa: N802 + parts = urlsplit(self.path) + outer.requests.append(self.path) + status, body = handle(parts.path, parse_qs(parts.query)) + data = json.dumps(body).encode() + self.send_response(status) + self.send_header("Content-Type", "application/fhir+json") + self.send_header("Content-Length", str(len(data))) + self.end_headers() + self.wfile.write(data) + + def log_message(self, *args: Any) -> None: + pass + + self.httpd = ThreadingHTTPServer(("127.0.0.1", 0), H) + self.url = f"http://127.0.0.1:{self.httpd.server_address[1]}/fhir" + threading.Thread(target=self.httpd.serve_forever, daemon=True).start() + + def close(self) -> None: + self.httpd.shutdown() + + +@pytest.fixture +def serve(): + servers: list[Server] = [] + + def make(handle: Callable[[str, dict[str, list[str]]], tuple[int, Any]]) -> Server: + servers.append(Server(handle)) + return servers[-1] + + yield make + for s in servers: + s.close() + + +PEOPLE = [patient(str(i), "Ana", f"Name{i}", "1990-04-09") for i in range(7)] + + +def paged(page_size: int, people: list[dict[str, Any]], total: Optional[int] = None): + def handle(path: str, query: dict[str, list[str]]) -> tuple[int, Any]: + page = int(query.get("_page", ["0"])[0]) + if "birthdate" in query: + match = [p for p in people if p["birthDate"] == query["birthdate"][0]] + elif "family" in query: + f = query["family"][0].lower() + match = [p for p in people if p["name"][0]["family"].lower().startswith(f)] + else: + match = [] + chunk = match[page * page_size : (page + 1) * page_size] + links = [{"relation": "self", "url": "Patient"}] + if (page + 1) * page_size < len(match): + links.append( + { + "relation": "next", + "url": f"Patient?_page={page + 1}&" + + "&".join(f"{k}={v[0]}" for k, v in query.items() if k != "_page"), + } + ) + return 200, { + "resourceType": "Bundle", + "type": "searchset", + "total": len(match) if total is None else total, + "link": links, + "entry": [{"resource": r} for r in chunk], + } + + return handle + + +def test_fhir_reads_every_page_and_finds_the_chart_on_the_last(serve) -> None: + people = PEOPLE + [patient("99", "Rosalind", "Jesswood", "1990-04-09")] + server = serve(paged(3, people)) + result = check_duplicate(FhirPatientSource(server.url), WANT) + assert result.decision == "exact" + assert result.candidates[0].record.mrn == "P99" + assert result.pages >= 3 + + +def test_fhir_no_match_proceeds(serve) -> None: + server = serve(paged(3, PEOPLE)) + result = check_duplicate(FhirPatientSource(server.url), WANT) + assert result.decision == "none", result.reason + + +@pytest.mark.parametrize( + "handle", + [ + paged(3, PEOPLE, total=9), # fewer read than the server reported + lambda p, q: (401, {"error": "invalid_token"}), + lambda p, q: (500, {}), + lambda p, q: (200, {"resourceType": "OperationOutcome"}), + lambda p, q: ( + 200, + { + "resourceType": "Bundle", + "entry": [{"resource": {"resourceType": "Observation"}}], + }, + ), + # A next link that never ends. + lambda p, q: ( + 200, + { + "resourceType": "Bundle", + "entry": [], + "link": [{"relation": "next", "url": "Patient?again=1"}], + }, + ), + ], +) +def test_fhir_fails_closed(serve, handle) -> None: + server = serve(handle) + assert check_duplicate(FhirPatientSource(server.url), WANT).decision == "unreadable" + + +def test_fhir_unreachable_fails_closed() -> None: + source = FhirPatientSource("http://127.0.0.1:9/fhir", timeout_s=0.5) + assert check_duplicate(source, WANT).decision == "unreadable" + + +def test_rest_full_page_without_next_link_fails_closed(serve) -> None: + rows = [ + {"uuid": str(i), "fname": "Ana", "lname": f"N{i}", "DOB": "1990-04-09"} + for i in range(5) + ] + server = serve(lambda p, q: (200, {"data": rows})) + source = RestPatientSource(server.url, path="/api/patient", page_size=5) + assert check_duplicate(source, WANT).decision == "unreadable" + source = RestPatientSource(server.url, path="/api/patient", page_size=50) + assert check_duplicate(source, WANT).decision == "none" + + +def test_sql_fallback(tmp_path: Path) -> None: + db = tmp_path / "openemr.db" + con = sqlite3.connect(db) + con.execute( + "CREATE TABLE patient_data (pid INTEGER, fname TEXT, lname TEXT, " + "DOB TEXT, pubpid TEXT)" + ) + con.execute( + "INSERT INTO patient_data VALUES (4, 'Rosalind', 'Jesswood-Okafor', " + "'1990-04-09', '4')" + ) + con.commit() + con.close() + result = check_duplicate(SqlPatientSource(lambda: sqlite3.connect(db)), WANT) + assert result.decision == "close" and result.channel == "sql" + + +# -- in the replayer -------------------------------------------------------------- + + +def _replayer_check(source: Any, params: dict[str, str]) -> tuple[bool, str]: + from openadapt_flow.ir import Postcondition, PostconditionKind + from openadapt_flow.runtime.replayer import Replayer + from tests.test_no_duplicate_candidate import MARKER, _Backend + from tests.test_replayer import FakeVision + + # The screen would stop: its list shows a namesake. + backend = _Backend(["5\tJESSWOOD, Ana\t1961-01-01\tFemale"]) + replayer = Replayer(backend, vision=FakeVision(), duplicate_record_source=source) + replayer._current_params = dict(params) + pc = Postcondition( + kind=PostconditionKind.NO_DUPLICATE_CANDIDATE, + text=MARKER, + candidate_rows="tr.oneresult", + candidate_frame_path=("#modalframe",), + candidate_params=("lname", "DOB"), + ) + passed = replayer._postcondition_passes(pc, b"", None, {}) + assert backend.read == [] # the screen list is not read + return passed, replayer._describe_postcondition(pc) + + +def test_the_replayer_asks_the_record_when_a_channel_is_configured() -> None: + table = TableSource([rec("Ana", "Jesswood", "1961-01-01", "5")]) + assert _replayer_check(table, WANT)[0] is True + table.records.append(rec("Rosalind", "Okafor", "1990-04-09", "8")) + passed, why = _replayer_check(table, WANT) + assert passed is False + assert "record check via table: close" in why + assert "close record 8 (same date of birth and first name" in why + + +def test_the_replayer_fails_closed_when_the_channel_raises() -> None: + class Broken: + channel = "fhir" + + def by_birthdate(self, dob: str) -> list[PatientRecord]: + raise RuntimeError("boom") + + passed, why = _replayer_check(Broken(), WANT) + assert passed is False and "unreadable" in why From 78e33a4eec446f6c3a57a62edbe4ff1d6d46109f Mon Sep 17 00:00:00 2001 From: abrichr Date: Sat, 24 Oct 2026 19:54:52 -0400 Subject: [PATCH 02/13] duplicate check: nicknames, swapped names, prior names, contact tie-breakers The first version missed every married name with a mistyped day of birth; in each, the phone, street and email matched the chart. The record check now also searches by first name, phone and email, knows common nicknames, swapped first and last names and the chart's birth name, and uses shared contact details to tie a mistyped date of birth to a chart. Every new match stops for a person. Extended seed-7 stream (622 items, 169 true duplicates): misses 60 -> 24, all 24 a name change plus a move that shares nothing with the chart; false stops unchanged at 2 (0.3 per 100 items). Screen: 252 false stops, 30 misses. Co-Authored-By: Claude Opus 5.5 --- CAPABILITIES.md | 2 +- llms-full.txt | 2 +- packages/flow/benchmark/dup_check/README.md | 85 +- packages/flow/benchmark/dup_check/measure.py | 148 +- .../dup_check/results/seed7-500.json | 1691 +++++++++++------ .../dup_check/results/soak-seed7-55.json | 1165 +++++++++--- packages/flow/docs/DUPLICATE_CHECK.md | 10 +- .../runtime/checks/duplicate_record.py | 365 +++- .../flow/openadapt_flow/runtime/replayer.py | 2 +- .../flow/tests/test_duplicate_record_check.py | 99 +- 10 files changed, 2673 insertions(+), 896 deletions(-) diff --git a/CAPABILITIES.md b/CAPABILITIES.md index bfefe6f42..d6adc674d 100644 --- a/CAPABILITIES.md +++ b/CAPABILITIES.md @@ -229,7 +229,7 @@ improvising or retrying when something is off. | Repair by default: once a repair agent is configured, every stop that wrote nothing goes to it without anyone starting it; a stop after a possible write goes to reconciliation; every stop is a row in a ledger with the funnel from stop to resumed item | On main ([#1360](https://github.com/OpenAdaptAI/OpenAdapt/pull/1360)) | Synthetic (MockMed, 22 drift modes, real Claude agent: 6 of 21 repairable stops resumed and verified, 0 wrong saves in 66 runs); routing over recorded OpenEMR stops; live openIMIS relabel: 3 of 3 reached repair, 0 resumed; silent-saves 1.1.0 UI-change blocks: 18 of 28 items that always stopped now correct, 0 wrong | `run` (on by default, `--no-auto-repair`), `repair:` in `deploy.yaml`, [benchmark/auto_repair](packages/flow/benchmark/auto_repair/README.md) | | Repair a relabeled save button: prove from the recorded identity hash that only the label changed (same control id, kind and form), re-qualify the fix on the new release, and have a person admit the certified copy afresh, since a save button's identity is part of the record check | On main | Real app (local OpenEMR, synthetic data): after the save button was relabeled, the stopped item re-ran and saved once after 8.3 operator minutes and 763 s of re-qualification; 896 s from stop to done ([#1513](https://github.com/OpenAdaptAI/OpenAdapt/pull/1513)) | `openadapt_flow/repair/relabel.py`, [SELF_REPAIR.md](packages/flow/docs/SELF_REPAIR.md#a-relabeled-control-proof-that-only-the-label-changed) | | A duplicate check that reads the app's match list (`no_duplicate_candidate`): stop only on a listed record with the item's last name or date of birth, fail closed on an unreadable or unloaded list | On main | Real app (local OpenEMR, synthetic data): false stops per 100 items 70.4 before, 16.4 after; 0 silent wrongs in 30 saves ([#1513](https://github.com/OpenAdaptAI/OpenAdapt/pull/1513)) | [DUPLICATE_CHECK.md](packages/flow/docs/DUPLICATE_CHECK.md) | -| A duplicate check that asks the system of record (FHIR `Patient` search or OpenEMR's patient API, every page; SQL as a documented fallback): stop on an exact or close match, proceed otherwise, fail closed on any read problem. The default when `effects` reads FHIR or OpenEMR's patient API | Open PR | Offline on the soak's seed-7 stream with planted true duplicates: false stops per 100 items 44.8 (screen) against 0.4 (record) on 563 items; 12 of 110 true duplicates missed by both (a married name with a mistyped date of birth). Not yet run on live OpenEMR | [DUPLICATE_CHECK.md](packages/flow/docs/DUPLICATE_CHECK.md) | +| A duplicate check that asks the system of record (FHIR `Patient` search or OpenEMR's patient API, every page; SQL as a documented fallback): stop on an exact or close match, proceed otherwise, fail closed on any read problem. The default when `effects` reads FHIR or OpenEMR's patient API | Open PR | Offline on the soak's seed-7 stream with planted true duplicates: false stops per 100 items 40.5 (screen) against 0.3 (record) on 622 items; true duplicates missed: screen 30, record 24 of 169, every record miss a name change plus a move that shares no name, contact or date with the chart. Not yet run on live OpenEMR | [DUPLICATE_CHECK.md](packages/flow/docs/DUPLICATE_CHECK.md) | | A switched-off `problems_only` gate asks a person before the save instead of stopping the item | On main | Synthetic; real app in [#1513](https://github.com/OpenAdaptAI/OpenAdapt/pull/1513) | [APPROVAL_MODES.md](packages/flow/docs/APPROVAL_MODES.md#when-the-mode-turns-itself-off) | | Fleet drift patches: a verified fix with no patient data, shared across sites under policy | Released, local reference hub | Simulator | `openadapt-flow fleet`, [FLEET_DRIFT_PATCHES.md](packages/flow/docs/FLEET_DRIFT_PATCHES.md) | diff --git a/llms-full.txt b/llms-full.txt index ccf95dda9..a43b25628 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -626,7 +626,7 @@ improvising or retrying when something is off. | Repair by default: once a repair agent is configured, every stop that wrote nothing goes to it without anyone starting it; a stop after a possible write goes to reconciliation; every stop is a row in a ledger with the funnel from stop to resumed item | On main ([#1360](https://github.com/OpenAdaptAI/OpenAdapt/pull/1360)) | Synthetic (MockMed, 22 drift modes, real Claude agent: 6 of 21 repairable stops resumed and verified, 0 wrong saves in 66 runs); routing over recorded OpenEMR stops; live openIMIS relabel: 3 of 3 reached repair, 0 resumed; silent-saves 1.1.0 UI-change blocks: 18 of 28 items that always stopped now correct, 0 wrong | `run` (on by default, `--no-auto-repair`), `repair:` in `deploy.yaml`, [benchmark/auto_repair](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/benchmark/auto_repair/README.md) | | Repair a relabeled save button: prove from the recorded identity hash that only the label changed (same control id, kind and form), re-qualify the fix on the new release, and have a person admit the certified copy afresh, since a save button's identity is part of the record check | On main | Real app (local OpenEMR, synthetic data): after the save button was relabeled, the stopped item re-ran and saved once after 8.3 operator minutes and 763 s of re-qualification; 896 s from stop to done ([#1513](https://github.com/OpenAdaptAI/OpenAdapt/pull/1513)) | `openadapt_flow/repair/relabel.py`, [SELF_REPAIR.md](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/SELF_REPAIR.md#a-relabeled-control-proof-that-only-the-label-changed) | | A duplicate check that reads the app's match list (`no_duplicate_candidate`): stop only on a listed record with the item's last name or date of birth, fail closed on an unreadable or unloaded list | On main | Real app (local OpenEMR, synthetic data): false stops per 100 items 70.4 before, 16.4 after; 0 silent wrongs in 30 saves ([#1513](https://github.com/OpenAdaptAI/OpenAdapt/pull/1513)) | [DUPLICATE_CHECK.md](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/DUPLICATE_CHECK.md) | -| A duplicate check that asks the system of record (FHIR `Patient` search or OpenEMR's patient API, every page; SQL as a documented fallback): stop on an exact or close match, proceed otherwise, fail closed on any read problem. The default when `effects` reads FHIR or OpenEMR's patient API | Open PR | Offline on the soak's seed-7 stream with planted true duplicates: false stops per 100 items 44.8 (screen) against 0.4 (record) on 563 items; 12 of 110 true duplicates missed by both (a married name with a mistyped date of birth). Not yet run on live OpenEMR | [DUPLICATE_CHECK.md](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/DUPLICATE_CHECK.md) | +| A duplicate check that asks the system of record (FHIR `Patient` search or OpenEMR's patient API, every page; SQL as a documented fallback): stop on an exact or close match, proceed otherwise, fail closed on any read problem. The default when `effects` reads FHIR or OpenEMR's patient API | Open PR | Offline on the soak's seed-7 stream with planted true duplicates: false stops per 100 items 40.5 (screen) against 0.3 (record) on 622 items; true duplicates missed: screen 30, record 24 of 169, every record miss a name change plus a move that shares no name, contact or date with the chart. Not yet run on live OpenEMR | [DUPLICATE_CHECK.md](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/DUPLICATE_CHECK.md) | | A switched-off `problems_only` gate asks a person before the save instead of stopping the item | On main | Synthetic; real app in [#1513](https://github.com/OpenAdaptAI/OpenAdapt/pull/1513) | [APPROVAL_MODES.md](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/APPROVAL_MODES.md#when-the-mode-turns-itself-off) | | Fleet drift patches: a verified fix with no patient data, shared across sites under policy | Released, local reference hub | Simulator | `openadapt-flow fleet`, [FLEET_DRIFT_PATCHES.md](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/FLEET_DRIFT_PATCHES.md) | diff --git a/packages/flow/benchmark/dup_check/README.md b/packages/flow/benchmark/dup_check/README.md index 6bcecd554..3148f3a81 100644 --- a/packages/flow/benchmark/dup_check/README.md +++ b/packages/flow/benchmark/dup_check/README.md @@ -30,28 +30,65 @@ married name plus a mistyped day of birth. Each item meets the existing charts, every earlier item's right outcome from the soak's truth, and another clerk's chart for `concurrent_same`. -## Results (2026-10-24) - -| stream | items | new-chart items | true duplicates | screen: false stops per 100 items | record: false stops per 100 items | screen: missed | record: missed | -|---|---:|---:|---:|---:|---:|---:|---:| -| soak-seed7-55 + planted | 118 | 45 | 71 | 8.5 (10) | 0.0 (0) | 12 | 12 | -| seed7-500 + planted | 563 | 440 | 110 | 44.8 (252) | 0.4 (2) | 12 | 12 | - -- **Screen false stops** are new patients who share a last name or date of - birth with any chart, including charts earlier items created. On the long - stream, 151 of 252 are `clean` items that met a namesake: the 300-surname - pool fills up. -- **Record false stops:** 2, both `shared_dob` items whose first name equals - that of the chart with the same date of birth. The married-name rule stops - them for a person, by design. -- **Missed, both checks:** all 12 are the held-out `heldout_married_dob_typo`. - A married name with a mistyped date of birth shares neither the last name - nor the date of birth with the chart, so neither check sees it. The other - held-out kinds (first-name and last-name typos) were caught by the record - check, and by the screen through the shared date of birth. -- **Record decisions** on the long stream: 450 `none`, 37 `exact`, 63 - `close`, 13 `unreadable`. The `unreadable` ones are the 13 referrals with - no date of birth, a right stop. +## Results (2026-10-24, second round) + +The first version of the record check missed 12 of 110 planted duplicates, +all of one kind: a married name with a mistyped day of birth. In every one, +the phone, street and email matched the chart exactly. So the second round +adds: +- searches by first name, phone and email; +- a nickname table; +- swapped first and last names; +- the chart's prior names (OpenEMR's `birth_fname`/`birth_lname`, or FHIR + `name.use` old or maiden); +- contact details (phone, email, street) as tie-breakers for a mistyped date + of birth. + +It also adds the kinds those rules target (`r2_`). Two `limit_` kinds share +nothing with the chart but the person, so no rule here can catch them. The +`r2_` rules were written knowing the `r2_` kinds, so those catches are not a +blind test. + +Extended stream (500 seed-7 items, plus the planted and extra charts): 622 +items, 440 new charts, 169 true duplicates. + +| check | false stops (per 100 items) | missed duplicates | +|---|---:|---:| +| screen (`no_duplicate_candidate` on the list) | 252 (40.5) | 30 | +| record, first version | 2 (0.3) | 60 | +| record, second round | 2 (0.3) | 24 | + +The soak's own 55 items, with the same planting (177 items, 130 true +duplicates): screen 10 false stops (5.6 per 100), 30 missed; record 0 false +stops, 24 missed. + +Missed duplicates by kind, extended stream: + +| kind | planted | screen | record v1 | record v2 | field that would have caught it | +|---|---:|---:|---:|---:|---| +| married name + mistyped day (`heldout_married_dob_typo`) | 12 | 12 | 12 | 0 | phone, street, email (caught by v2) | +| first and last name swapped (`r2_name_swap`) | 12 | 0 | 12 | 0 | the names, crossed (v2) | +| nickname, Bob for Robert (`r2_nickname`) | 6 | 0 | 6 | 0 | the nickname table (v2) | +| prior name on the chart, moved, mistyped day (`r2_prior_name_moved_dob_typo`) | 6 | 6 | 6 | 0 | the chart's birth name (v2) | +| year digits transposed (`r2_year_transposed`) | 11 | 0 | 0 | 0 | the date of birth, one field off | +| married name, moved, mistyped day (`limit_married_moved_dob_typo`) | 12 | 12 | 12 | 12 | none in the referral: needs an MRN, an insurance id or a person | +| new first and last name, moved, same day (`limit_new_name_moved_same_dob`) | 12 | 0 | 12 | 12 | none in the referral; the screen stops it only because it stops every shared date of birth | + +**The trade-off.** Misses fell from 60 to 24, and every remaining miss is a +`limit_` kind, while false stops stayed at 2 of 440 new charts. Both are +`shared_dob` items whose first name equals that of the chart with the same +birth date, which the married-name rule sends to a person on purpose. The +screen still stops `limit_new_name_moved_same_dob`, and the record check +doesn't: a full name change and a move, with nothing but the birth date in +common, looks like any new patient who shares a birthday. Stopping it would +mean stopping every shared date of birth, which is 33 of the screen's 252 +false stops. + +One rule was narrowed during this round. "Another name, one shared contact +detail and a near date of birth" stopped two new patients, because the +generator's phone pool has only 100 numbers (`555-01xx`). It now needs two +shared contact details for a near date of birth, or one for the same date. +That narrowing is tuned to this stream, and a real phone pool is far larger. ## Limits @@ -63,6 +100,10 @@ the soak's truth, and another clerk's chart for `concurrent_same`. - The close-match rules were written with the soak's generator in view (`ambiguous_same_person` changes the day of birth). Only the held-out kinds are a test of rules not tuned to them. +- The search for prior names assumes OpenEMR's FHIR `family` search also + matches a patient's old names. The SQL fallback does (`birth_lname`); the + live run must confirm FHIR. `patient_history`'s previous names are not read + yet. - One seed. The screen rule is the `no_duplicate_candidate` rule applied to OpenEMR's listing as the soak describes it (any chart sharing the first name, the last name or the date of birth); it is not a run of the browser. diff --git a/packages/flow/benchmark/dup_check/measure.py b/packages/flow/benchmark/dup_check/measure.py index 75dcfb3b8..6892875b0 100644 --- a/packages/flow/benchmark/dup_check/measure.py +++ b/packages/flow/benchmark/dup_check/measure.py @@ -58,7 +58,23 @@ "heldout_first_typo", "heldout_last_typo", "heldout_married_dob_typo", + "r2_nickname", + "r2_name_swap", + "r2_year_transposed", + "r2_prior_name_moved_dob_typo", + "limit_married_moved_dob_typo", + "limit_new_name_moved_same_dob", } +#: Formal first names given to extra existing charts, and the nickname each +#: is referred under (``r2_nickname``). +FORMAL = ( + ("Robert", "Bob"), + ("Elizabeth", "Liz"), + ("William", "Bill"), + ("Katherine", "Kate"), + ("Theodore", "Theo"), + ("Margaret", "Peggy"), +) MARRIED = ("Okafor", "Lindqvist", "Brennan", "Moreau", "Castellanos", "Whitcombe") @@ -169,15 +185,135 @@ def plant(existing: list[dict[str, str]], items: list[Item], seed: int) -> list[ [], ) ) + planted += round_two(n, person) out = list(items) for p in planted: out.insert(rng.randint(0, len(out)), p) return out +def moved(person: dict[str, str], n: int) -> dict[str, str]: + """The same person at a new address, phone and email.""" + + return dict( + person, + street=f"{900 + n} NEW PLACE", + phone_home=f"555-02{n:02d}", + email=f"moved.{n}@example.invalid", + ) + + +def day_typo(dob: str) -> str: + y, m, d = dob.split("-") + return f"{y}-{m}-{int(d) % 28 + 1:02d}" + + +def round_two(n: int, person: dict[str, str]) -> list[Item]: + """Kinds added after the first measurement (rules written for them, + except the two ``limit_`` kinds, which no rule here can catch).""" + + out = [ + Item( + f"r2-sw-{n}", + "r2_name_swap", + dict(person, fname=person["lname"], lname=person["fname"]), + None, + [], + ) + ] + y, m, d = person["DOB"].split("-") + ty = y[:2] + y[3] + y[2] + if ty != y and 1900 <= int(ty) <= 2010: + out.append( + Item( + f"r2-ty-{n}", + "r2_year_transposed", + dict(person, DOB=f"{ty}-{m}-{d}"), + None, + [], + ) + ) + out.append( + Item( + f"lim-mm-{n}", + "limit_married_moved_dob_typo", + dict( + moved(person, n), + lname=MARRIED[(n + 3) % len(MARRIED)], + DOB=day_typo(person["DOB"]), + ), + None, + [], + ) + ) + out.append( + Item( + f"lim-nn-{n}", + "limit_new_name_moved_same_dob", + dict(moved(person, n), fname="Unrelated", lname="Renamed"), + None, + [], + ) + ) + return out + + +def extra_existing( + existing: list[dict[str, str]], +) -> tuple[list[dict[str, str]], list[Item]]: + """Charts with formal first names or a stored prior name, and their + true-duplicate referrals.""" + + extra, items = [], [] + for n, (formal, nick) in enumerate(FORMAL): + base = existing[n % len(existing)] + chart = dict( + base, + fname=formal, + lname=f"Formal{n}", + email=f"formal.{n}@example.invalid", + phone_home=f"555-03{n:02d}", + street=f"{700 + n} FORMAL ROAD", + ) + extra.append(chart) + items.append( + Item(f"r2-nk-{n}", "r2_nickname", dict(chart, fname=nick), None, []) + ) + for n in range(6): + base = existing[(n + 3) % len(existing)] + chart = dict( + base, + lname=f"Married{n}", + prior_lname=f"Maiden{n}", + email=f"prior.{n}@example.invalid", + phone_home=f"555-04{n:02d}", + street=f"{800 + n} PRIOR LANE", + DOB=base["DOB"], + ) + extra.append(chart) + referral = dict( + moved(chart, 50 + n), lname=f"Maiden{n}", DOB=day_typo(chart["DOB"]) + ) + referral.pop("prior_lname") + items.append( + Item(f"r2-pn-{n}", "r2_prior_name_moved_dob_typo", referral, None, []) + ) + return extra, items + + def record_of(i: int, person: dict[str, str]) -> PatientRecord: + prior = ( + ((person["fname"], person["prior_lname"]),) if person.get("prior_lname") else () + ) return PatientRecord( - str(i), person["fname"], person["lname"], person.get("DOB", "") + str(i), + person["fname"], + person["lname"], + person.get("DOB", ""), + phone=person.get("phone_home", ""), + street=person.get("street", ""), + email=person.get("email", ""), + prior_names=prior, ) @@ -274,10 +410,12 @@ def main() -> int: args = parser.parse_args() args.out.mkdir(parents=True, exist_ok=True) runs = {} - existing, items = from_ledger(SOAK / "workload.jsonl") - runs["soak-seed7-55"] = measure(existing, plant(existing, items, 7)) - existing, items = from_generator(7, args.long_items, 10) - runs[f"seed7-{args.long_items}"] = measure(existing, plant(existing, items, 7)) + for name, (existing, items) in ( + ("soak-seed7-55", from_ledger(SOAK / "workload.jsonl")), + (f"seed7-{args.long_items}", from_generator(7, args.long_items, 10)), + ): + extra, extra_items = extra_existing(existing) + runs[name] = measure(existing + extra, plant(existing, items + extra_items, 7)) for name, run in runs.items(): (args.out / f"{name}.json").write_text( json.dumps(run, indent=2, sort_keys=True) + "\n" diff --git a/packages/flow/benchmark/dup_check/results/seed7-500.json b/packages/flow/benchmark/dup_check/results/seed7-500.json index af7f2cb31..c377d3749 100644 --- a/packages/flow/benchmark/dup_check/results/seed7-500.json +++ b/packages/flow/benchmark/dup_check/results/seed7-500.json @@ -189,21 +189,21 @@ "screen": "proceed" }, { - "exception": "ambiguous_same_person", - "item": "ref-00024", + "exception": "heldout_married_dob_typo", + "item": "held-md-9", "record": "close", "record_why": [ - "same name, date of birth one field off" + "same first name, another last name, same phone, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { - "exception": "planted_married", - "item": "plant-m-1", + "exception": "ambiguous_same_person", + "item": "ref-00024", "record": "close", "record_why": [ - "same date of birth and first name, another last name" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -308,26 +308,6 @@ "right": "new chart", "screen": "proceed" }, - { - "exception": "planted_hyphenated", - "item": "plant-h-1", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_last_typo", - "item": "held-l-2", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, { "exception": "clean", "item": "ref-00037", @@ -344,6 +324,16 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "planted_hyphenated", + "item": "plant-h-1", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00039", @@ -352,6 +342,17 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "heldout_first_typo", + "item": "held-f-7", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "shared_dob", "item": "ref-00040", @@ -394,22 +395,13 @@ "right": "no chart: duplicate", "screen": "stop" }, - { - "exception": "planted_hyphenated", - "item": "plant-h-5", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, { "exception": "dup_existing", "item": "ref-00045", "record": "exact", "record_why": [ - "same first name, last name and date of birth" + "same first name, last name and date of birth", + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" @@ -423,16 +415,8 @@ "screen": "proceed" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-1", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-4", + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-2", "record": "none", "record_why": [], "right": "no chart: duplicate", @@ -462,6 +446,26 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "r2_name_swap", + "item": "r2-sw-2", + "record": "close", + "record_why": [ + "first and last name swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-0", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00050", @@ -483,7 +487,8 @@ "item": "ref-00052", "record": "exact", "record_why": [ - "same first name, last name and date of birth" + "same first name, last name and date of birth", + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" @@ -504,6 +509,16 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "planted_hyphenated", + "item": "plant-h-10", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "missing_required", "item": "ref-00055", @@ -513,76 +528,88 @@ "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-7", + "exception": "shared_dob", + "item": "ref-00056", "record": "none", "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" + "right": "new chart", + "screen": "stop" }, { - "exception": "shared_dob", - "item": "ref-00056", + "exception": "clean", + "item": "ref-00057", "record": "none", "record_why": [], "right": "new chart", - "screen": "stop" + "screen": "proceed" }, { - "exception": "planted_married", - "item": "plant-m-4", + "exception": "r2_year_transposed", + "item": "r2-ty-8", "record": "close", "record_why": [ - "same date of birth and first name, another last name" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, { "exception": "planted_hyphenated", - "item": "plant-h-2", + "item": "plant-h-4", "record": "close", "record_why": [ + "same date of birth and first name, another last name", "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-7", + "exception": "failed_save", + "item": "ref-00058", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "planted_married", + "item": "plant-m-2", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "clean", - "item": "ref-00057", + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-0", "record": "none", "record_why": [], - "right": "new chart", + "right": "no chart: duplicate", "screen": "proceed" }, { - "exception": "heldout_first_typo", - "item": "held-f-4", + "exception": "r2_name_swap", + "item": "r2-sw-8", "record": "close", "record_why": [ - "same date of birth, a similar name" + "first and last name swapped" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "failed_save", - "item": "ref-00058", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" + "exception": "planted_married", + "item": "plant-m-4", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" }, { "exception": "clean", @@ -592,6 +619,16 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "heldout_last_typo", + "item": "held-l-2", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00060", @@ -600,6 +637,17 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "planted_hyphenated", + "item": "plant-h-8", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00061", @@ -624,22 +672,13 @@ "right": "new chart", "screen": "stop" }, - { - "exception": "heldout_last_typo", - "item": "held-l-11", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, { "exception": "dup_existing", "item": "ref-00064", "record": "exact", "record_why": [ - "same first name, last name and date of birth" + "same first name, last name and date of birth", + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" @@ -652,6 +691,17 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "heldout_first_typo", + "item": "held-f-6", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00066", @@ -661,12 +711,10 @@ "screen": "proceed" }, { - "exception": "planted_swapped_dob", - "item": "plant-s-3", - "record": "close", - "record_why": [ - "same name, day and month swapped" - ], + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-10", + "record": "none", + "record_why": [], "right": "no chart: duplicate", "screen": "stop" }, @@ -686,6 +734,27 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "heldout_last_typo", + "item": "held-l-7", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "r2_name_swap", + "item": "r2-sw-1", + "record": "close", + "record_why": [ + "first and last name swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00069", @@ -712,16 +781,6 @@ "right": "new chart", "screen": "proceed" }, - { - "exception": "planted_swapped_dob", - "item": "plant-s-10", - "record": "close", - "record_why": [ - "same name, day and month swapped" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, { "exception": "clean", "item": "ref-00072", @@ -738,6 +797,26 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-5", + "record": "close", + "record_why": [ + "same first name, another last name, same phone, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-0", + "record": "close", + "record_why": [ + "same first name, another last name, same phone, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, { "exception": "clean", "item": "ref-00074", @@ -770,16 +849,6 @@ "right": "new chart", "screen": "proceed" }, - { - "exception": "heldout_first_typo", - "item": "held-f-0", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, { "exception": "clean", "item": "ref-00078", @@ -796,6 +865,17 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "heldout_first_typo", + "item": "held-f-8", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "common_surname", "item": "ref-00080", @@ -820,20 +900,12 @@ "right": "new chart", "screen": "proceed" }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-2", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, { "exception": "ambiguous_same_person", "item": "ref-00083", "record": "close", "record_why": [ - "same name, date of birth one field off" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -847,8 +919,8 @@ "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-3", + "exception": "heldout_first_typo", + "item": "held-f-1", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -875,14 +947,12 @@ "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-7", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-1", + "record": "none", + "record_why": [], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "clean", @@ -902,6 +972,14 @@ "right": "no chart: duplicate", "screen": "stop" }, + { + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-3", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00089", @@ -934,14 +1012,6 @@ "right": "no chart", "screen": "stop" }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-6", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, { "exception": "clean", "item": "ref-00093", @@ -960,6 +1030,34 @@ "right": "no chart: duplicate", "screen": "stop" }, + { + "exception": "r2_name_swap", + "item": "r2-sw-3", + "record": "close", + "record_why": [ + "first and last name swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "r2_name_swap", + "item": "r2-sw-0", + "record": "close", + "record_why": [ + "first and last name swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-9", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "shared_dob", "item": "ref-00095", @@ -1018,14 +1116,6 @@ "right": "new chart", "screen": "stop" }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-11", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, { "exception": "clean", "item": "ref-00102", @@ -1034,6 +1124,17 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "heldout_last_typo", + "item": "held-l-6", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "missing_optional", "item": "ref-00103", @@ -1050,6 +1151,16 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "heldout_last_typo", + "item": "held-l-10", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00105", @@ -1074,16 +1185,6 @@ "right": "new chart", "screen": "stop" }, - { - "exception": "planted_hyphenated", - "item": "plant-h-6", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, { "exception": "shared_dob", "item": "ref-00108", @@ -1092,6 +1193,17 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "heldout_first_typo", + "item": "held-f-3", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "dup_resend", "item": "ref-00109", @@ -1102,6 +1214,16 @@ "right": "no chart: duplicate", "screen": "stop" }, + { + "exception": "r2_name_swap", + "item": "r2-sw-11", + "record": "close", + "record_why": [ + "first and last name swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00110", @@ -1149,7 +1271,7 @@ "item": "ref-00115", "record": "close", "record_why": [ - "same name, date of birth one field off" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -1162,9 +1284,17 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "malformed_dob", + "item": "ref-00117", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, { "exception": "planted_hyphenated", - "item": "plant-h-4", + "item": "plant-h-2", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -1172,14 +1302,6 @@ "right": "no chart: duplicate", "screen": "stop" }, - { - "exception": "malformed_dob", - "item": "ref-00117", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, { "exception": "common_surname", "item": "ref-00118", @@ -1220,16 +1342,6 @@ "right": "new chart", "screen": "stop" }, - { - "exception": "heldout_first_typo", - "item": "held-f-5", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, { "exception": "clean", "item": "ref-00123", @@ -1238,6 +1350,14 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-2", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "dup_resend", "item": "ref-00124", @@ -1309,27 +1429,35 @@ "item": "ref-00132", "record": "none", "record_why": [], - "right": "new chart", + "right": "new chart", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00133", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-6", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_married", - "item": "plant-m-6", + "exception": "planted_swapped_dob", + "item": "plant-s-3", "record": "close", "record_why": [ - "same date of birth and first name, another last name" + "same name, day and month swapped" ], "right": "no chart: duplicate", "screen": "stop" }, - { - "exception": "clean", - "item": "ref-00133", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, { "exception": "clean", "item": "ref-00134", @@ -1346,6 +1474,16 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "planted_married", + "item": "plant-m-11", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00136", @@ -1394,6 +1532,14 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-9", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, { "exception": "slow_save", "item": "ref-00142", @@ -1426,6 +1572,16 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "r2_year_transposed", + "item": "r2-ty-6", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00146", @@ -1498,6 +1654,16 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "planted_hyphenated", + "item": "plant-h-0", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00155", @@ -1575,11 +1741,12 @@ "screen": "proceed" }, { - "exception": "planted_married", - "item": "plant-m-10", + "exception": "heldout_first_typo", + "item": "held-f-5", "record": "close", "record_why": [ - "same date of birth and first name, another last name" + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -1600,16 +1767,6 @@ "right": "new chart", "screen": "proceed" }, - { - "exception": "planted_married", - "item": "plant-m-0", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, { "exception": "clean", "item": "ref-00166", @@ -1626,16 +1783,6 @@ "right": "new chart", "screen": "stop" }, - { - "exception": "heldout_last_typo", - "item": "held-l-6", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, { "exception": "clean", "item": "ref-00168", @@ -1644,12 +1791,22 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-3", + "record": "close", + "record_why": [ + "same first name, another last name, same phone, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, { "exception": "ambiguous_same_person", "item": "ref-00169", "record": "close", "record_why": [ - "same name, date of birth one field off" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -1681,21 +1838,21 @@ "screen": "stop" }, { - "exception": "planted_married", - "item": "plant-m-7", - "record": "close", + "exception": "dup_resend", + "item": "ref-00173", + "record": "exact", "record_why": [ - "same date of birth and first name, another last name" + "same first name, last name and date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "dup_resend", - "item": "ref-00173", - "record": "exact", + "exception": "r2_year_transposed", + "item": "r2-ty-3", + "record": "close", "record_why": [ - "same first name, last name and date of birth" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -1772,16 +1929,6 @@ "right": "new chart", "screen": "stop" }, - { - "exception": "planted_married", - "item": "plant-m-2", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, { "exception": "shared_dob", "item": "ref-00183", @@ -1798,6 +1945,16 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "planted_swapped_dob", + "item": "plant-s-10", + "record": "close", + "record_why": [ + "same name, day and month swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "ambiguous_new_person", "item": "ref-00185", @@ -1806,16 +1963,6 @@ "right": "new chart", "screen": "stop" }, - { - "exception": "planted_married", - "item": "plant-m-8", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, { "exception": "dup_resend", "item": "ref-00186", @@ -1834,6 +1981,16 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "planted_swapped_dob", + "item": "plant-s-4", + "record": "close", + "record_why": [ + "same name, day and month swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "session_expiry", "item": "ref-00188", @@ -1879,7 +2036,7 @@ "item": "ref-00193", "record": "close", "record_why": [ - "same name, date of birth one field off" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -1942,16 +2099,6 @@ "right": "new chart", "screen": "stop" }, - { - "exception": "heldout_last_typo", - "item": "held-l-0", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, { "exception": "concurrent_other", "item": "ref-00201", @@ -2032,16 +2179,6 @@ "right": "new chart", "screen": "stop" }, - { - "exception": "planted_married", - "item": "plant-m-5", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, { "exception": "shared_dob", "item": "ref-00211", @@ -2051,14 +2188,14 @@ "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-2", + "exception": "heldout_married_dob_typo", + "item": "held-md-10", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same first name, another last name, same phone, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "clean", @@ -2077,11 +2214,11 @@ "screen": "stop" }, { - "exception": "planted_swapped_dob", - "item": "plant-s-4", + "exception": "r2_year_transposed", + "item": "r2-ty-2", "record": "close", "record_why": [ - "same name, day and month swapped" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -2094,6 +2231,16 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "planted_married", + "item": "plant-m-1", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00215", @@ -2110,6 +2257,16 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "heldout_first_typo", + "item": "held-f-2", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "common_surname", "item": "ref-00217", @@ -2126,16 +2283,6 @@ "right": "new chart", "screen": "proceed" }, - { - "exception": "planted_hyphenated", - "item": "plant-h-10", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, { "exception": "session_expiry", "item": "ref-00219", @@ -2160,6 +2307,17 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "heldout_last_typo", + "item": "held-l-5", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00222", @@ -2184,14 +2342,6 @@ "right": "new chart", "screen": "proceed" }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-9", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, { "exception": "clean", "item": "ref-00225", @@ -2216,12 +2366,24 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "planted_hyphenated", + "item": "plant-h-5", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "dup_existing", "item": "ref-00228", "record": "exact", "record_why": [ - "same first name, last name and date of birth" + "same first name, last name and date of birth", + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" @@ -2277,14 +2439,14 @@ "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-3", + "exception": "heldout_married_dob_typo", + "item": "held-md-11", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same first name, another last name, same phone, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "malformed_case", @@ -2310,6 +2472,27 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "heldout_last_typo", + "item": "held-l-8", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "r2_year_transposed", + "item": "r2-ty-1", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "failed_save", "item": "ref-00238", @@ -2384,6 +2567,16 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "heldout_first_typo", + "item": "held-f-10", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "concurrent_other", "item": "ref-00247", @@ -2400,6 +2593,16 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "planted_hyphenated", + "item": "plant-h-9", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "shared_dob", "item": "ref-00249", @@ -2488,16 +2691,6 @@ "right": "new chart", "screen": "stop" }, - { - "exception": "heldout_first_typo", - "item": "held-f-11", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, { "exception": "concurrent_other", "item": "ref-00260", @@ -2522,6 +2715,17 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "planted_hyphenated", + "item": "plant-h-6", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00263", @@ -2578,16 +2782,6 @@ "right": "new chart", "screen": "stop" }, - { - "exception": "heldout_last_typo", - "item": "held-l-1", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, { "exception": "clean", "item": "ref-00270", @@ -2604,16 +2798,6 @@ "right": "new chart", "screen": "stop" }, - { - "exception": "heldout_first_typo", - "item": "held-f-10", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, { "exception": "clean", "item": "ref-00272", @@ -2631,24 +2815,12 @@ "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-5", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_last_typo", - "item": "held-l-9", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-8", + "record": "none", + "record_why": [], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "ambiguous_new_person", @@ -2666,6 +2838,16 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "r2_name_swap", + "item": "r2-sw-5", + "record": "close", + "record_why": [ + "first and last name swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00276", @@ -2682,6 +2864,28 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "planted_married", + "item": "plant-m-3", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_married", + "item": "plant-m-5", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00278", @@ -2698,6 +2902,16 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "r2_name_swap", + "item": "r2-sw-7", + "record": "close", + "record_why": [ + "first and last name swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "missing_optional", "item": "ref-00280", @@ -2770,14 +2984,6 @@ "right": "new chart", "screen": "proceed" }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-8", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, { "exception": "clean", "item": "ref-00289", @@ -2786,16 +2992,6 @@ "right": "new chart", "screen": "stop" }, - { - "exception": "heldout_first_typo", - "item": "held-f-6", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, { "exception": "clean", "item": "ref-00290", @@ -2820,6 +3016,16 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "r2_name_swap", + "item": "r2-sw-4", + "record": "close", + "record_why": [ + "first and last name swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "dup_resend", "item": "ref-00293", @@ -2830,6 +3036,17 @@ "right": "no chart: duplicate", "screen": "stop" }, + { + "exception": "heldout_last_typo", + "item": "held-l-3", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00294", @@ -2859,7 +3076,7 @@ "item": "ref-00297", "record": "close", "record_why": [ - "same name, date of birth one field off" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -2877,7 +3094,8 @@ "item": "ref-00299", "record": "exact", "record_why": [ - "same first name, last name and date of birth" + "same first name, last name and date of birth", + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" @@ -2895,7 +3113,7 @@ "item": "ref-00301", "record": "close", "record_why": [ - "same name, date of birth one field off" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -2916,6 +3134,26 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "heldout_last_typo", + "item": "held-l-9", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "r2_year_transposed", + "item": "r2-ty-7", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00304", @@ -2956,6 +3194,16 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "r2_year_transposed", + "item": "r2-ty-9", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00309", @@ -2964,6 +3212,14 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-6", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, { "exception": "concurrent_other", "item": "ref-00310", @@ -2972,16 +3228,6 @@ "right": "new chart", "screen": "stop" }, - { - "exception": "planted_married", - "item": "plant-m-11", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, { "exception": "clean", "item": "ref-00311", @@ -2990,6 +3236,14 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-7", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, { "exception": "clean", "item": "ref-00312", @@ -3014,6 +3268,14 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-5", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00315", @@ -3144,14 +3406,6 @@ "right": "new chart", "screen": "stop" }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-0", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, { "exception": "malformed_dob", "item": "ref-00331", @@ -3160,6 +3414,16 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "planted_married", + "item": "plant-m-0", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "ambiguous_new_person", "item": "ref-00332", @@ -3176,6 +3440,16 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "r2_year_transposed", + "item": "r2-ty-10", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00334", @@ -3184,16 +3458,6 @@ "right": "new chart", "screen": "proceed" }, - { - "exception": "heldout_first_typo", - "item": "held-f-9", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, { "exception": "slow_save", "item": "ref-00335", @@ -3226,6 +3490,44 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "heldout_first_typo", + "item": "held-f-9", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-4", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "r2_name_swap", + "item": "r2-sw-10", + "record": "close", + "record_why": [ + "first and last name swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-11", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00339", @@ -3322,16 +3624,6 @@ "right": "new chart", "screen": "proceed" }, - { - "exception": "planted_hyphenated", - "item": "plant-h-7", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, { "exception": "ambiguous_new_person", "item": "ref-00351", @@ -3364,6 +3656,14 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-3", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, { "exception": "clean", "item": "ref-00355", @@ -3438,6 +3738,14 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-11", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, { "exception": "concurrent_other", "item": "ref-00364", @@ -3486,6 +3794,16 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "r2_year_transposed", + "item": "r2-ty-0", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00370", @@ -3598,6 +3916,37 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "planted_hyphenated", + "item": "plant-h-7", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-11", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "r2_name_swap", + "item": "r2-sw-6", + "record": "close", + "record_why": [ + "first and last name swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "common_surname", "item": "ref-00384", @@ -3622,16 +3971,6 @@ "right": "new chart", "screen": "proceed" }, - { - "exception": "heldout_last_typo", - "item": "held-l-4", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, { "exception": "clean", "item": "ref-00387", @@ -3656,6 +3995,16 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-2", + "record": "close", + "record_why": [ + "same first name, another last name, same phone, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, { "exception": "clean", "item": "ref-00390", @@ -3696,6 +4045,16 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "planted_married", + "item": "plant-m-9", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00395", @@ -3744,16 +4103,6 @@ "right": "new chart", "screen": "stop" }, - { - "exception": "heldout_last_typo", - "item": "held-l-8", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, { "exception": "common_surname", "item": "ref-00401", @@ -3767,11 +4116,32 @@ "item": "ref-00402", "record": "exact", "record_why": [ - "same first name, last name and date of birth" + "same first name, last name and date of birth", + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-0", + "record": "close", + "record_why": [ + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-4", + "record": "close", + "record_why": [ + "same first name, another last name, same phone, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, { "exception": "missing_required", "item": "ref-00403", @@ -3781,12 +4151,14 @@ "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-5", - "record": "none", - "record_why": [], + "exception": "r2_name_swap", + "item": "r2-sw-9", + "record": "close", + "record_why": [ + "first and last name swapped" + ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "clean", @@ -3801,7 +4173,18 @@ "item": "ref-00405", "record": "close", "record_why": [ - "same name, date of birth one field off" + "same name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-3", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -3830,6 +4213,17 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "planted_married", + "item": "plant-m-8", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00409", @@ -3854,16 +4248,6 @@ "right": "no chart", "screen": "stop" }, - { - "exception": "planted_hyphenated", - "item": "plant-h-11", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, { "exception": "missing_required", "item": "ref-00412", @@ -3881,31 +4265,22 @@ "screen": "stop" }, { - "exception": "clean", - "item": "ref-00414", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "heldout_first_typo", - "item": "held-f-1", + "exception": "planted_married", + "item": "plant-m-6", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_hyphenated", - "item": "plant-h-3", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", + "exception": "clean", + "item": "ref-00414", + "record": "none", + "record_why": [], + "right": "new chart", "screen": "stop" }, { @@ -3924,14 +4299,6 @@ "right": "new chart", "screen": "stop" }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-3", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, { "exception": "concurrent_other", "item": "ref-00417", @@ -3940,6 +4307,16 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-1", + "record": "close", + "record_why": [ + "same first name, another last name, same phone, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, { "exception": "clean", "item": "ref-00418", @@ -3956,6 +4333,24 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-1", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-8", + "record": "close", + "record_why": [ + "same first name, another last name, same phone, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, { "exception": "clean", "item": "ref-00420", @@ -3974,16 +4369,6 @@ "right": "no chart: duplicate", "screen": "stop" }, - { - "exception": "planted_hyphenated", - "item": "plant-h-9", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, { "exception": "clean", "item": "ref-00422", @@ -3992,6 +4377,14 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-10", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, { "exception": "clean", "item": "ref-00423", @@ -4010,6 +4403,14 @@ "right": "no chart: duplicate", "screen": "stop" }, + { + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-4", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, { "exception": "clean", "item": "ref-00425", @@ -4018,6 +4419,16 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "planted_married", + "item": "plant-m-10", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "ambiguous_new_person", "item": "ref-00426", @@ -4055,16 +4466,7 @@ "item": "ref-00430", "record": "exact", "record_why": [ - "same first name, last name and date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "planted_married", - "item": "plant-m-3", - "record": "close", - "record_why": [ + "same first name, last name and date of birth", "same date of birth and first name, another last name" ], "right": "no chart: duplicate", @@ -4075,7 +4477,8 @@ "item": "ref-00431", "record": "exact", "record_why": [ - "same first name, last name and date of birth" + "same first name, last name and date of birth", + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" @@ -4088,16 +4491,6 @@ "right": "new chart", "screen": "proceed" }, - { - "exception": "planted_married", - "item": "plant-m-9", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, { "exception": "shared_dob", "item": "ref-00433", @@ -4106,6 +4499,16 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "heldout_last_typo", + "item": "held-l-1", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00434", @@ -4122,6 +4525,16 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "r2_year_transposed", + "item": "r2-ty-4", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00436", @@ -4135,7 +4548,7 @@ "item": "ref-00437", "record": "close", "record_why": [ - "same name, date of birth one field off" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -4172,6 +4585,17 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "planted_married", + "item": "plant-m-7", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00442", @@ -4180,6 +4604,14 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-7", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "shared_dob", "item": "ref-00443", @@ -4270,14 +4702,6 @@ "right": "new chart", "screen": "stop" }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-10", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, { "exception": "session_expiry", "item": "ref-00454", @@ -4302,6 +4726,14 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-5", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, { "exception": "clean", "item": "ref-00457", @@ -4350,6 +4782,14 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-11", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00463", @@ -4366,6 +4806,16 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "heldout_last_typo", + "item": "held-l-11", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00465", @@ -4382,16 +4832,6 @@ "right": "new chart", "screen": "stop" }, - { - "exception": "planted_hyphenated", - "item": "plant-h-8", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, { "exception": "clean", "item": "ref-00467", @@ -4410,6 +4850,16 @@ "right": "no chart: duplicate", "screen": "stop" }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-6", + "record": "close", + "record_why": [ + "same first name, another last name, same phone, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, { "exception": "clean", "item": "ref-00469", @@ -4418,6 +4868,17 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "heldout_first_typo", + "item": "held-f-4", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "missing_required", "item": "ref-00470", @@ -4489,7 +4950,7 @@ "item": "ref-00478", "record": "close", "record_why": [ - "same name, date of birth one field off" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -4538,16 +4999,6 @@ "right": "new chart", "screen": "proceed" }, - { - "exception": "heldout_last_typo", - "item": "held-l-10", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, { "exception": "common_surname", "item": "ref-00484", @@ -4556,16 +5007,6 @@ "right": "new chart", "screen": "stop" }, - { - "exception": "planted_hyphenated", - "item": "plant-h-0", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, { "exception": "clean", "item": "ref-00485", @@ -4582,6 +5023,16 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "r2_year_transposed", + "item": "r2-ty-5", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00487", @@ -4695,39 +5146,187 @@ "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-8", + "exception": "r2_nickname", + "item": "r2-nk-0", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "r2_nickname", + "item": "r2-nk-1", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "r2_nickname", + "item": "r2-nk-2", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "r2_nickname", + "item": "r2-nk-3", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-7", + "record": "close", + "record_why": [ + "same first name, another last name, same phone, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-4", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "r2_nickname", + "item": "r2-nk-4", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "r2_nickname", + "item": "r2-nk-5", "record": "close", "record_why": [ "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" + }, + { + "exception": "r2_prior_name_moved_dob_typo", + "item": "r2-pn-0", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth (a prior name on the chart)" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "r2_prior_name_moved_dob_typo", + "item": "r2-pn-1", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth (a prior name on the chart)" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "r2_prior_name_moved_dob_typo", + "item": "r2-pn-2", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth (a prior name on the chart)" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "r2_prior_name_moved_dob_typo", + "item": "r2-pn-3", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth (a prior name on the chart)" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-8", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "r2_prior_name_moved_dob_typo", + "item": "r2-pn-4", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth (a prior name on the chart)" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-0", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "r2_prior_name_moved_dob_typo", + "item": "r2-pn-5", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth (a prior name on the chart)" + ], + "right": "no chart: duplicate", + "screen": "proceed" } ], "summary": { - "items": 563, + "items": 622, "new_chart_items": 440, "record": { - "caught_duplicates": 98, + "caught_duplicates": 145, "decisions": { - "close": 63, + "close": 110, "exact": 37, - "none": 450, + "none": 462, "unreadable": 13 }, "false_stops": 2, "false_stops_by_exception": { "shared_dob": 2 }, - "false_stops_per_100_items": 0.4, + "false_stops_per_100_items": 0.3, "missed_by_exception": { - "heldout_married_dob_typo": 12 + "limit_married_moved_dob_typo": 12, + "limit_new_name_moved_same_dob": 12 }, - "missed_duplicates": 12 + "missed_duplicates": 24 }, "screen": { - "caught_duplicates": 98, + "caught_duplicates": 139, "false_stops": 252, "false_stops_by_exception": { "ambiguous_new_person": 7, @@ -4742,12 +5341,14 @@ "shared_dob": 33, "slow_save": 5 }, - "false_stops_per_100_items": 44.8, + "false_stops_per_100_items": 40.5, "missed_by_exception": { - "heldout_married_dob_typo": 12 + "heldout_married_dob_typo": 12, + "limit_married_moved_dob_typo": 12, + "r2_prior_name_moved_dob_typo": 6 }, - "missed_duplicates": 12 + "missed_duplicates": 30 }, - "true_duplicates": 110 + "true_duplicates": 169 } } diff --git a/packages/flow/benchmark/dup_check/results/soak-seed7-55.json b/packages/flow/benchmark/dup_check/results/soak-seed7-55.json index f442065c1..2b3fc8b38 100644 --- a/packages/flow/benchmark/dup_check/results/soak-seed7-55.json +++ b/packages/flow/benchmark/dup_check/results/soak-seed7-55.json @@ -25,16 +25,16 @@ "screen": "proceed" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-0", + "exception": "clean", + "item": "ref-00004", "record": "none", "record_why": [], - "right": "no chart: duplicate", + "right": "new chart", "screen": "proceed" }, { "exception": "heldout_last_typo", - "item": "held-l-2", + "item": "held-l-1", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -43,40 +43,50 @@ "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-6", + "exception": "r2_year_transposed", + "item": "r2-ty-3", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_married", - "item": "plant-m-1", + "exception": "shared_dob", + "item": "ref-00005", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-6", "record": "close", "record_why": [ - "same date of birth and first name, another last name" + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_hyphenated", - "item": "plant-h-11", + "exception": "heldout_married_dob_typo", + "item": "held-md-3", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same first name, another last name, same phone, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "planted_hyphenated", "item": "plant-h-6", "record": "close", "record_why": [ + "same date of birth and first name, another last name", "same date of birth, a similar name" ], "right": "no chart: duplicate", @@ -84,23 +94,46 @@ }, { "exception": "heldout_married_dob_typo", - "item": "held-md-10", - "record": "none", - "record_why": [], + "item": "held-md-9", + "record": "close", + "record_why": [ + "same first name, another last name, same phone, a mistyped date of birth" + ], "right": "no chart: duplicate", "screen": "proceed" }, { - "exception": "clean", - "item": "ref-00004", + "exception": "malformed_dob", + "item": "ref-00006", "record": "none", "record_why": [], "right": "new chart", "screen": "proceed" }, { - "exception": "heldout_first_typo", - "item": "held-f-5", + "exception": "planted_hyphenated", + "item": "plant-h-3", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-2", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-0", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -110,7 +143,7 @@ }, { "exception": "planted_hyphenated", - "item": "plant-h-4", + "item": "plant-h-11", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -119,38 +152,47 @@ "screen": "stop" }, { - "exception": "shared_dob", - "item": "ref-00005", + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-5", "record": "none", "record_why": [], - "right": "new chart", - "screen": "stop" + "right": "no chart: duplicate", + "screen": "proceed" }, { - "exception": "planted_swapped_dob", - "item": "plant-s-10", + "exception": "planted_married", + "item": "plant-m-11", "record": "close", "record_why": [ - "same name, day and month swapped" + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_swapped_dob", - "item": "plant-s-3", + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-1", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "r2_name_swap", + "item": "r2-sw-10", "record": "close", "record_why": [ - "same name, day and month swapped" + "first and last name swapped" ], "right": "no chart: duplicate", "screen": "stop" }, { "exception": "planted_hyphenated", - "item": "plant-h-9", + "item": "plant-h-5", "record": "close", "record_why": [ + "same date of birth and first name, another last name", "same date of birth, a similar name" ], "right": "no chart: duplicate", @@ -158,7 +200,7 @@ }, { "exception": "planted_married", - "item": "plant-m-2", + "item": "plant-m-1", "record": "close", "record_why": [ "same date of birth and first name, another last name" @@ -167,32 +209,24 @@ "screen": "stop" }, { - "exception": "malformed_dob", - "item": "ref-00006", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "planted_married", - "item": "plant-m-8", + "exception": "r2_name_swap", + "item": "r2-sw-9", "record": "close", "record_why": [ - "same date of birth and first name, another last name" + "first and last name swapped" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-3", + "exception": "heldout_married_dob_typo", + "item": "held-md-8", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same first name, another last name, same phone, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "clean", @@ -203,49 +237,42 @@ "screen": "proceed" }, { - "exception": "planted_married", - "item": "plant-m-7", + "exception": "r2_year_transposed", + "item": "r2-ty-10", "record": "close", "record_why": [ - "same date of birth and first name, another last name" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, { "exception": "heldout_last_typo", - "item": "held-l-7", + "item": "held-l-4", "record": "close", "record_why": [ + "same date of birth and first name, another last name", "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-2", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "heldout_first_typo", - "item": "held-f-4", + "exception": "r2_year_transposed", + "item": "r2-ty-7", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-11", + "exception": "planted_married", + "item": "plant-m-2", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" @@ -259,53 +286,92 @@ "screen": "stop" }, { - "exception": "planted_married", - "item": "plant-m-10", + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-3", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_swapped_dob", + "item": "plant-s-4", "record": "close", "record_why": [ - "same date of birth and first name, another last name" + "same name, day and month swapped" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_married", - "item": "plant-m-9", + "exception": "heldout_married_dob_typo", + "item": "held-md-1", "record": "close", "record_why": [ - "same date of birth and first name, another last name" + "same first name, another last name, same phone, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "r2_name_swap", + "item": "r2-sw-2", + "record": "close", + "record_why": [ + "first and last name swapped" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "clean", - "item": "ref-00009", - "record": "none", - "record_why": [], - "right": "new chart", + "exception": "heldout_married_dob_typo", + "item": "held-md-0", + "record": "close", + "record_why": [ + "same first name, another last name, same phone, a mistyped date of birth" + ], + "right": "no chart: duplicate", "screen": "proceed" }, { - "exception": "planted_hyphenated", - "item": "plant-h-0", + "exception": "r2_name_swap", + "item": "r2-sw-5", "record": "close", "record_why": [ - "same date of birth, a similar name" + "first and last name swapped" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-1", + "exception": "r2_name_swap", + "item": "r2-sw-8", "record": "close", "record_why": [ - "same date of birth, a similar name" + "first and last name swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_married", + "item": "plant-m-5", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" }, + { + "exception": "clean", + "item": "ref-00009", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, { "exception": "clean", "item": "ref-00010", @@ -314,6 +380,16 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "r2_year_transposed", + "item": "r2-ty-0", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00011", @@ -340,6 +416,16 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "planted_hyphenated", + "item": "plant-h-9", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00014", @@ -357,18 +443,34 @@ "screen": "proceed" }, { - "exception": "heldout_first_typo", - "item": "held-f-6", + "exception": "clean", + "item": "ref-00016", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "r2_name_swap", + "item": "r2-sw-3", "record": "close", "record_why": [ - "same date of birth, a similar name" + "first and last name swapped" ], "right": "no chart: duplicate", "screen": "stop" }, + { + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-8", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", - "item": "ref-00016", + "item": "ref-00017", "record": "none", "record_why": [], "right": "new chart", @@ -376,30 +478,22 @@ }, { "exception": "clean", - "item": "ref-00017", + "item": "ref-00018", "record": "none", "record_why": [], "right": "new chart", "screen": "proceed" }, { - "exception": "heldout_last_typo", - "item": "held-l-4", + "exception": "r2_year_transposed", + "item": "r2-ty-2", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, - { - "exception": "clean", - "item": "ref-00018", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, { "exception": "malformed_dob", "item": "ref-00019", @@ -408,6 +502,16 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "planted_hyphenated", + "item": "plant-h-0", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00020", @@ -417,30 +521,31 @@ "screen": "proceed" }, { - "exception": "heldout_first_typo", - "item": "held-f-8", + "exception": "r2_name_swap", + "item": "r2-sw-4", "record": "close", "record_why": [ - "same date of birth, a similar name" + "first and last name swapped" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_hyphenated", - "item": "plant-h-7", + "exception": "heldout_married_dob_typo", + "item": "held-md-7", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same first name, another last name, same phone, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "planted_married", - "item": "plant-m-0", + "item": "plant-m-4", "record": "close", "record_why": [ + "same date of birth and first name, another last name", "same date of birth and first name, another last name" ], "right": "no chart: duplicate", @@ -457,28 +562,34 @@ "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-3", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], + "exception": "clean", + "item": "ref-00022", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-1", + "record": "none", + "record_why": [], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_hyphenated", - "item": "plant-h-10", + "exception": "r2_year_transposed", + "item": "r2-ty-5", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, { "exception": "heldout_first_typo", - "item": "held-f-2", + "item": "held-f-1", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -487,11 +598,13 @@ "screen": "stop" }, { - "exception": "clean", - "item": "ref-00022", - "record": "none", - "record_why": [], - "right": "new chart", + "exception": "heldout_married_dob_typo", + "item": "held-md-6", + "record": "close", + "record_why": [ + "same first name, another last name, same phone, a mistyped date of birth" + ], + "right": "no chart: duplicate", "screen": "proceed" }, { @@ -502,22 +615,12 @@ "right": "new chart", "screen": "proceed" }, - { - "exception": "heldout_first_typo", - "item": "held-f-0", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, { "exception": "ambiguous_same_person", "item": "ref-00024", "record": "close", "record_why": [ - "same name, date of birth one field off" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -555,32 +658,30 @@ "screen": "proceed" }, { - "exception": "concurrent_same", - "item": "ref-00029", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth" - ], + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-10", + "record": "none", + "record_why": [], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { - "exception": "heldout_first_typo", - "item": "held-f-1", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-11", + "record": "none", + "record_why": [], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-11", - "record": "none", - "record_why": [], + "exception": "concurrent_same", + "item": "ref-00029", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "common_surname", @@ -591,11 +692,11 @@ "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-7", + "exception": "r2_name_swap", + "item": "r2-sw-7", "record": "close", "record_why": [ - "same date of birth, a similar name" + "first and last name swapped" ], "right": "no chart: duplicate", "screen": "stop" @@ -617,11 +718,12 @@ "screen": "proceed" }, { - "exception": "planted_married", - "item": "plant-m-6", + "exception": "heldout_last_typo", + "item": "held-l-7", "record": "close", "record_why": [ - "same date of birth and first name, another last name" + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -634,6 +736,27 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "heldout_first_typo", + "item": "held-f-11", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-4", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "dup_existing", "item": "ref-00034", @@ -653,41 +776,51 @@ "screen": "proceed" }, { - "exception": "clean", - "item": "ref-00036", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" + "exception": "heldout_last_typo", + "item": "held-l-3", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" }, { "exception": "clean", - "item": "ref-00037", + "item": "ref-00036", "record": "none", "record_why": [], "right": "new chart", "screen": "proceed" }, { - "exception": "heldout_first_typo", - "item": "held-f-10", + "exception": "heldout_last_typo", + "item": "held-l-8", "record": "close", "record_why": [ + "same date of birth and first name, another last name", "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_married", - "item": "plant-m-11", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-9", + "record": "none", + "record_why": [], "right": "no chart: duplicate", "screen": "stop" }, + { + "exception": "clean", + "item": "ref-00037", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, { "exception": "slow_save", "item": "ref-00038", @@ -697,26 +830,40 @@ "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-0", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], + "exception": "clean", + "item": "ref-00039", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-7", + "record": "none", + "record_why": [], "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "shared_dob", + "item": "ref-00040", + "record": "none", + "record_why": [], + "right": "new chart", "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-1", + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-5", "record": "none", "record_why": [], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "planted_married", - "item": "plant-m-4", + "item": "plant-m-10", "record": "close", "record_why": [ "same date of birth and first name, another last name" @@ -725,16 +872,19 @@ "screen": "stop" }, { - "exception": "clean", - "item": "ref-00039", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" + "exception": "heldout_first_typo", + "item": "held-f-6", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-11", + "exception": "planted_hyphenated", + "item": "plant-h-10", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -744,71 +894,54 @@ }, { "exception": "shared_dob", - "item": "ref-00040", + "item": "ref-00041", "record": "none", "record_why": [], "right": "new chart", "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-9", + "exception": "heldout_first_typo", + "item": "held-f-7", "record": "close", "record_why": [ + "same date of birth and first name, another last name", "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-8", + "exception": "planted_married", + "item": "plant-m-0", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-9", + "exception": "planted_married", + "item": "plant-m-9", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-7", + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-0", "record": "none", "record_why": [], "right": "no chart: duplicate", "screen": "proceed" }, { - "exception": "planted_hyphenated", - "item": "plant-h-3", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "shared_dob", - "item": "ref-00041", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "planted_hyphenated", - "item": "plant-h-8", + "exception": "heldout_first_typo", + "item": "held-f-2", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -825,19 +958,20 @@ "screen": "proceed" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-9", + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-6", "record": "none", "record_why": [], "right": "no chart: duplicate", "screen": "proceed" }, { - "exception": "planted_married", - "item": "plant-m-3", + "exception": "planted_hyphenated", + "item": "plant-h-8", "record": "close", "record_why": [ - "same date of birth and first name, another last name" + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -851,21 +985,76 @@ "screen": "proceed" }, { - "exception": "dup_resend", - "item": "ref-00044", - "record": "exact", + "exception": "heldout_last_typo", + "item": "held-l-5", + "record": "close", "record_why": [ - "same first name, last name and date of birth" + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-6", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", + "exception": "r2_year_transposed", + "item": "r2-ty-4", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-5", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-4", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "r2_year_transposed", + "item": "r2-ty-1", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "dup_resend", + "item": "ref-00044", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-4", + "record": "close", + "record_why": [ + "same first name, another last name, same phone, a mistyped date of birth" + ], + "right": "no chart: duplicate", "screen": "proceed" }, { @@ -873,16 +1062,46 @@ "item": "ref-00045", "record": "exact", "record_why": [ - "same first name, last name and date of birth" + "same first name, last name and date of birth", + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-10", + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-11", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-5", + "record": "close", + "record_why": [ + "same first name, another last name, same phone, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "r2_name_swap", + "item": "r2-sw-1", + "record": "close", + "record_why": [ + "first and last name swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-7", "record": "close", "record_why": [ + "same date of birth and first name, another last name", "same date of birth, a similar name" ], "right": "no chart: duplicate", @@ -897,8 +1116,8 @@ "screen": "proceed" }, { - "exception": "planted_hyphenated", - "item": "plant-h-1", + "exception": "heldout_last_typo", + "item": "held-l-9", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -915,16 +1134,16 @@ "screen": "proceed" }, { - "exception": "clean", - "item": "ref-00048", + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-3", "record": "none", "record_why": [], - "right": "new chart", + "right": "no chart: duplicate", "screen": "proceed" }, { "exception": "clean", - "item": "ref-00049", + "item": "ref-00048", "record": "none", "record_why": [], "right": "new chart", @@ -932,15 +1151,44 @@ }, { "exception": "heldout_married_dob_typo", - "item": "held-md-8", + "item": "held-md-10", + "record": "close", + "record_why": [ + "same first name, another last name, same phone, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-8", "record": "none", "record_why": [], "right": "no chart: duplicate", "screen": "proceed" }, { - "exception": "heldout_last_typo", - "item": "held-l-5", + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-10", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_married", + "item": "plant-m-6", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-0", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -949,11 +1197,11 @@ "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-5", + "exception": "clean", + "item": "ref-00049", "record": "none", "record_why": [], - "right": "no chart: duplicate", + "right": "new chart", "screen": "proceed" }, { @@ -973,18 +1221,27 @@ "screen": "proceed" }, { - "exception": "planted_hyphenated", - "item": "plant-h-5", - "record": "close", + "exception": "dup_existing", + "item": "ref-00052", + "record": "exact", "record_why": [ - "same date of birth, a similar name" + "same first name, last name and date of birth", + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" }, + { + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-4", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "planted_swapped_dob", - "item": "plant-s-4", + "item": "plant-s-3", "record": "close", "record_why": [ "same name, day and month swapped" @@ -993,19 +1250,12 @@ "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-3", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "dup_existing", - "item": "ref-00052", - "record": "exact", + "exception": "heldout_first_typo", + "item": "held-f-3", + "record": "close", "record_why": [ - "same first name, last name and date of birth" + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -1019,10 +1269,21 @@ "screen": "proceed" }, { - "exception": "planted_hyphenated", - "item": "plant-h-2", + "exception": "heldout_first_typo", + "item": "held-f-10", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-8", "record": "close", "record_why": [ + "same date of birth and first name, another last name", "same date of birth, a similar name" ], "right": "no chart: duplicate", @@ -1038,9 +1299,10 @@ }, { "exception": "planted_married", - "item": "plant-m-5", + "item": "plant-m-3", "record": "close", "record_why": [ + "same date of birth and first name, another last name", "same date of birth and first name, another last name" ], "right": "no chart: duplicate", @@ -1048,12 +1310,44 @@ }, { "exception": "heldout_married_dob_typo", - "item": "held-md-4", - "record": "none", - "record_why": [], + "item": "held-md-2", + "record": "close", + "record_why": [ + "same first name, another last name, same phone, a mistyped date of birth" + ], "right": "no chart: duplicate", "screen": "proceed" }, + { + "exception": "heldout_first_typo", + "item": "held-f-9", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-2", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "r2_year_transposed", + "item": "r2-ty-6", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "missing_required", "item": "ref-00055", @@ -1061,29 +1355,328 @@ "record_why": [], "right": "no chart", "screen": "stop" + }, + { + "exception": "r2_nickname", + "item": "r2-nk-0", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-1", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "r2_nickname", + "item": "r2-nk-1", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-11", + "record": "close", + "record_why": [ + "same first name, another last name, same phone, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-2", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_married", + "item": "plant-m-7", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-2", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "r2_nickname", + "item": "r2-nk-2", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "r2_year_transposed", + "item": "r2-ty-8", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_married", + "item": "plant-m-8", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "r2_nickname", + "item": "r2-nk-3", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "r2_year_transposed", + "item": "r2-ty-9", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-6", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-10", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "r2_nickname", + "item": "r2-nk-4", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "r2_nickname", + "item": "r2-nk-5", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-9", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-4", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "r2_prior_name_moved_dob_typo", + "item": "r2-pn-0", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth (a prior name on the chart)" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "planted_swapped_dob", + "item": "plant-s-10", + "record": "close", + "record_why": [ + "same name, day and month swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "r2_prior_name_moved_dob_typo", + "item": "r2-pn-1", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth (a prior name on the chart)" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "r2_name_swap", + "item": "r2-sw-0", + "record": "close", + "record_why": [ + "first and last name swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-11", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-7", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "r2_prior_name_moved_dob_typo", + "item": "r2-pn-2", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth (a prior name on the chart)" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "r2_prior_name_moved_dob_typo", + "item": "r2-pn-3", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth (a prior name on the chart)" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "r2_prior_name_moved_dob_typo", + "item": "r2-pn-4", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth (a prior name on the chart)" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-0", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "r2_name_swap", + "item": "r2-sw-6", + "record": "close", + "record_why": [ + "first and last name swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "r2_prior_name_moved_dob_typo", + "item": "r2-pn-5", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth (a prior name on the chart)" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "r2_name_swap", + "item": "r2-sw-11", + "record": "close", + "record_why": [ + "first and last name swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" } ], "summary": { - "items": 118, + "items": 177, "new_chart_items": 45, "record": { - "caught_duplicates": 59, + "caught_duplicates": 106, "decisions": { - "close": 52, + "close": 99, "exact": 7, - "none": 57, + "none": 69, "unreadable": 2 }, "false_stops": 0, "false_stops_by_exception": {}, "false_stops_per_100_items": 0.0, "missed_by_exception": { - "heldout_married_dob_typo": 12 + "limit_married_moved_dob_typo": 12, + "limit_new_name_moved_same_dob": 12 }, - "missed_duplicates": 12 + "missed_duplicates": 24 }, "screen": { - "caught_duplicates": 59, + "caught_duplicates": 100, "false_stops": 10, "false_stops_by_exception": { "clean": 2, @@ -1091,12 +1684,14 @@ "shared_dob": 3, "slow_save": 1 }, - "false_stops_per_100_items": 8.5, + "false_stops_per_100_items": 5.6, "missed_by_exception": { - "heldout_married_dob_typo": 12 + "heldout_married_dob_typo": 12, + "limit_married_moved_dob_typo": 12, + "r2_prior_name_moved_dob_typo": 6 }, - "missed_duplicates": 12 + "missed_duplicates": 30 }, - "true_duplicates": 71 + "true_duplicates": 130 } } diff --git a/packages/flow/docs/DUPLICATE_CHECK.md b/packages/flow/docs/DUPLICATE_CHECK.md index 2b88b79e3..066b573dc 100644 --- a/packages/flow/docs/DUPLICATE_CHECK.md +++ b/packages/flow/docs/DUPLICATE_CHECK.md @@ -73,13 +73,14 @@ read-only SQL query on `patient_data` (`SqlPatientSource`) is the documented fallback for a deployment with neither; it is wired by hand, not by default. The check searches by the date of birth, by the date of birth with day and -month swapped, by each part of the last name, and by MRN when the run has one. +month swapped, by each part of the last and first name, by first name, and by +MRN, phone and email when the run has them. It reads every page, and it decides from whole records: | Decision | When | What happens | |---|---|---| | `exact` | same first name, last name and date of birth, or the same MRN | stop: the chart exists | -| `close` | same name with the date of birth one field off or day and month swapped; same date of birth with a similar name or a shared hyphenated part; same date of birth and first name under another last name | stop for a person; the halt names each record and why | +| `close` | same name (or a nickname, or the chart's prior name) with day and month swapped, or with the date of birth one field off and either the exact name or a shared phone, email or street; same date of birth with a similar name or a shared hyphenated part; first and last name swapped; same date of birth and first name under another last name; another name with the same date of birth and a shared contact detail, or a near date of birth and two | stop for a person; the halt names each record and why | | `none` | no record is this patient | proceed | | `unreadable` | unreachable, refused credentials, not a result set, a reported total that doesn't match what was read, more than 50 pages, a full REST page with no next link, a missing or non-ISO date of birth | stop (fail closed) | @@ -87,3 +88,8 @@ Sharing only a last name, or only a date of birth, doesn't stop the item. Measured offline on the production soak's seed-7 stream, with true duplicates planted: `benchmark/dup_check/README.md`. + +Not catchable from the referral alone: a person whose name changed, who moved, +and whose date of birth is mistyped or merely shared. Nothing the referral +carries ties them to the chart; that needs an MRN, an insurance id, or a +person. diff --git a/packages/flow/openadapt_flow/runtime/checks/duplicate_record.py b/packages/flow/openadapt_flow/runtime/checks/duplicate_record.py index d8e8c95f1..b3cbecc18 100644 --- a/packages/flow/openadapt_flow/runtime/checks/duplicate_record.py +++ b/packages/flow/openadapt_flow/runtime/checks/duplicate_record.py @@ -53,6 +53,11 @@ class PatientRecord: lname: str dob: str # ISO YYYY-MM-DD, or "" when the record has none mrn: str = "" + phone: str = "" + street: str = "" + email: str = "" + #: Earlier names, (first, last): a birth or maiden name, a previous name. + prior_names: tuple[tuple[str, str], ...] = () @dataclass @@ -93,6 +98,12 @@ def by_family(self, family: str) -> list[PatientRecord]: ... def by_identifier(self, mrn: str) -> list[PatientRecord]: ... + def by_given(self, given: str) -> list[PatientRecord]: ... + + def by_phone(self, phone: str) -> list[PatientRecord]: ... + + def by_email(self, email: str) -> list[PatientRecord]: ... + # -- matching ------------------------------------------------------------------- @@ -176,23 +187,188 @@ def one_field_off(a: str, b: str) -> bool: return sum(x != y for x, y in zip(pa, pb)) == 1 +#: Common English given-name equivalents. Generic, not drawn from any test +#: workload; a deployment can extend it (``NICKNAMES.update``). +_NICKNAME_GROUPS = ( + "robert bob bobby rob robbie", + "william bill billy will willie liam", + "elizabeth liz beth betty eliza lizzie libby", + "katherine kate kathy katie kat", + "catherine cathy cate kate", + "margaret maggie meg peggy marge greta", + "james jim jimmy jamie", + "john jack johnny jon", + "richard rick ricky dick rich", + "michael mike mikey mick", + "thomas tom tommy", + "joseph joe joey", + "charles charlie chuck chas", + "edward ed eddie ted ned", + "anthony tony", + "daniel dan danny", + "christopher chris kit", + "alexander alex sasha xander", + "alexandra alex sandra sasha lexi", + "patricia pat patty trish tricia", + "jennifer jen jenny", + "rebecca becky becca", + "susan sue susie suzy", + "deborah deb debbie", + "nicholas nick nicky", + "benjamin ben benny", + "samuel sam sammy", + "samantha sam sammie", + "matthew matt matty", + "andrew andy drew", + "stephen steve stevie", + "steven steve stevie", + "jonathan jon jonny", + "timothy tim timmy", + "theodore theo ted teddy", + "dorothy dot dottie dolly", + "victoria vicky tori", + "abigail abby gail", + "gregory greg", + "kenneth ken kenny", + "ronald ron ronnie", + "donald don donnie", + "lawrence larry laurie", + "frederick fred freddie", + "isabella bella izzy isabel", + "jacqueline jackie", + "josephine jo josie", + "eleanor ellie nora nell", + "francis frank frankie", + "frances fran frankie", + "henry hank harry hal", + "harold harry hal", + "raymond ray", + "rafael rafa", + "dmitri dima", + "anastasia ana stacy", + "ana anna annie", + "ines ine", + "nadia nadya", + "mateo mat teo", +) +NICKNAMES: dict[str, set[str]] = {} +for _group in _NICKNAME_GROUPS: + _names = set(_group.split()) + for _name in _names: + NICKNAMES.setdefault(_name, set()).update(_names) + + +def same_given(a: str, b: str) -> bool: + """The same first name: similar spelling, or a known nickname of it.""" + + if similar_name(a, b): + return True + x, y = norm_name(a), norm_name(b) + return bool(x and y and y in NICKNAMES.get(x, ())) + + +def dob_near(a: str, b: str) -> bool: + """A mistyped date of birth: one field off, day and month swapped, or two + digits of one field transposed (1984 and 1948).""" + + if not a or not b or a == b: + return False + return one_field_off(a, b) or swapped(a) == b + + +def _digits(value: str) -> str: + return re.sub(r"\D", "", value or "") + + +def _street(value: str) -> str: + return re.sub( + r"\s+", " ", re.sub(r"[^0-9a-z ]", "", (value or "").casefold()) + ).strip() + + +def contacts_shared(want: Mapping[str, str], record: PatientRecord) -> list[str]: + """Every contact detail the referral and the chart share.""" + + out = [] + phone = _digits(want.get("phone_home") or "") + if len(phone) >= 7 and phone == _digits(record.phone): + out.append("same phone") + email = (want.get("email") or "").strip().casefold() + if email and email == record.email.strip().casefold(): + out.append("same email") + street = _street(want.get("street") or "") + if street and street == _street(record.street): + out.append("same street address") + return out + + +def tie_breaker(want: Mapping[str, str], record: PatientRecord) -> Optional[str]: + """A contact detail the referral and the chart share, if any.""" + + phone = _digits(want.get("phone_home") or "") + if len(phone) >= 7 and phone == _digits(record.phone): + return "same phone" + email = (want.get("email") or "").strip().casefold() + if email and email == record.email.strip().casefold(): + return "same email" + street = _street(want.get("street") or "") + if street and street == _street(record.street): + return "same street address" + return None + + def classify(want: Mapping[str, str], record: PatientRecord) -> Optional[Candidate]: - """Is ``record`` this new patient (``want``: fname, lname, DOB, mrn)?""" + """Is ``record`` this new patient? + + ``want``: fname, lname, DOB, and when known mrn, phone_home, email, + street. The record's prior names are tried as well as its current one. + """ mrn = (want.get("mrn") or "").strip() if mrn and record.mrn and mrn == record.mrn.strip(): return Candidate(record, "exact", "same MRN") + names = [(record.fname, record.lname), *record.prior_names] + for i, (first, last) in enumerate(names): + found = _classify_name(want, record, first, last) + if found is not None: + if i: + found.why += " (a prior name on the chart)" + if found.match == "exact": + found.match = "close" + return found + # A full name change: only the contact details tie the two together. + shared = contacts_shared(want, record) + if ( + shared + and record.dob + and ( + record.dob == want["DOB"] + or (len(shared) >= 2 and dob_near(want["DOB"], record.dob)) + ) + ): + return Candidate( + record, + "close", + f"another name; {', '.join(shared)}; " + + ("the same" if record.dob == want["DOB"] else "a near") + + " date of birth", + ) + return None + + +def _classify_name( + want: Mapping[str, str], record: PatientRecord, first: str, last: str +) -> Optional[Candidate]: fname, lname, dob = want["fname"], want["lname"], want["DOB"] - same_first = norm_name(fname) == norm_name(record.fname) - same_last = norm_name(lname) == norm_name(record.lname) + same_first = norm_name(fname) == norm_name(first) + same_last = norm_name(lname) == norm_name(last) if same_first and same_last and dob == record.dob: return Candidate( record, "exact", "same first name, last name and date of birth" ) - first_like = similar_name(fname, record.fname) - last_like = similar_name(lname, record.lname) or bool( - name_parts(lname) & name_parts(record.lname) - ) + first_like = same_given(fname, first) + last_like = similar_name(lname, last) or bool(name_parts(lname) & name_parts(last)) + near = dob_near(dob, record.dob) if first_like and last_like: if not record.dob: return Candidate( @@ -202,12 +378,24 @@ def classify(want: Mapping[str, str], record: PatientRecord) -> Optional[Candida return Candidate(record, "close", "same date of birth, a similar name") if record.dob == swapped(dob): return Candidate(record, "close", "same name, day and month swapped") - if same_first and same_last and one_field_off(dob, record.dob): - return Candidate(record, "close", "same name, date of birth one field off") - if record.dob == dob and same_first: + if near and (same_first and same_last or tie_breaker(want, record)): + return Candidate(record, "close", "same name, a mistyped date of birth") + swapped_names = norm_name(fname) == norm_name(last) and norm_name( + lname + ) == norm_name(first) + if swapped_names and (record.dob == dob or near): + return Candidate(record, "close", "first and last name swapped") + if record.dob == dob and first_like: return Candidate( record, "close", "same date of birth and first name, another last name" ) + if near and first_like and tie_breaker(want, record): + return Candidate( + record, + "close", + f"same first name, another last name, {tie_breaker(want, record)}, " + "a mistyped date of birth", + ) return None @@ -232,9 +420,18 @@ def check_duplicate( ] if swapped(dob): queries.append((source.by_birthdate, swapped(dob))) # type: ignore[arg-type] - queries += [(source.by_family, part) for part in sorted(name_parts(lname))] + # Each part of the last name, and of the first name (a swapped name). + queries += [ + (source.by_family, part) + for part in sorted(name_parts(lname) | name_parts(fname)) + ] + queries.append((source.by_given, fname)) if (want.get("mrn") or "").strip(): queries.append((source.by_identifier, want["mrn"].strip())) + if len(_digits(want.get("phone_home") or "")) >= 7: + queries.append((source.by_phone, want["phone_home"].strip())) + if (want.get("email") or "").strip(): + queries.append((source.by_email, want["email"].strip())) seen: dict[str, PatientRecord] = {} try: for query, value in queries: @@ -372,12 +569,17 @@ def search(self, params: dict[str, str]) -> list[PatientRecord]: return out def _record(self, resource: dict[str, Any]) -> PatientRecord: - names = resource.get("name") or [] + names = [n for n in resource.get("name") or [] if isinstance(n, dict)] + current = [n for n in names if n.get("use") not in ("old", "maiden")] official = next( - (n for n in names if isinstance(n, dict) and n.get("use") == "official"), - names[0] if names and isinstance(names[0], dict) else {}, + (n for n in current if n.get("use") == "official"), + current[0] if current else {}, ) - given = official.get("given") or [] + + def pair(n: dict[str, Any]) -> tuple[str, str]: + given = n.get("given") or [] + return (str(given[0]) if given else "", str(n.get("family") or "")) + mrn = "" for ident in resource.get("identifier") or []: if not isinstance(ident, dict): @@ -390,12 +592,37 @@ def _record(self, resource: dict[str, Any]) -> PatientRecord: ): mrn = str(ident.get("value") or "") break + telecom = [t for t in resource.get("telecom") or [] if isinstance(t, dict)] + phone = next( + ( + str(t.get("value") or "") + for t in telecom + if t.get("system") == "phone" and t.get("use") in (None, "home") + ), + "", + ) + email = next( + (str(t.get("value") or "") for t in telecom if t.get("system") == "email"), + "", + ) + address = next( + (a for a in resource.get("address") or [] if isinstance(a, dict)), {} + ) + lines = address.get("line") or [] + first, last = pair(official) + prior = tuple( + pair(n) for n in names if n is not official and pair(n) != (first, last) + ) return PatientRecord( record_id=str(resource.get("id") or ""), - fname=str(given[0]) if given else "", - lname=str(official.get("family") or ""), + fname=first, + lname=last, dob=str(resource.get("birthDate") or ""), mrn=mrn, + phone=phone, + street=str(lines[0]) if lines else "", + email=email, + prior_names=prior, ) def by_birthdate(self, dob: str) -> list[PatientRecord]: @@ -407,6 +634,15 @@ def by_family(self, family: str) -> list[PatientRecord]: def by_identifier(self, mrn: str) -> list[PatientRecord]: return self.search({"identifier": mrn}) + def by_given(self, given: str) -> list[PatientRecord]: + return self.search({"given": given}) + + def by_phone(self, phone: str) -> list[PatientRecord]: + return self.search({"phone": phone}) + + def by_email(self, email: str) -> list[PatientRecord]: + return self.search({"email": email}) + class SqlPatientSource: """Fallback: a read-only query on OpenEMR's ``patient_data``. @@ -426,7 +662,7 @@ def __init__( self.table = table self.pages_read = 0 - def _query(self, where: str, value: str) -> list[PatientRecord]: + def _query(self, where: str, *values: str) -> list[PatientRecord]: try: conn = self._connect() except Exception as exc: # noqa: BLE001 @@ -434,8 +670,9 @@ def _query(self, where: str, value: str) -> list[PatientRecord]: try: cur = conn.cursor() cur.execute( - f"SELECT pid, fname, lname, DOB, pubpid FROM {self.table} WHERE {where}", - (value,), + "SELECT pid, fname, lname, DOB, pubpid, phone_home, street, email, " + f"birth_fname, birth_lname FROM {self.table} WHERE {where}", + values, ) rows = cur.fetchall() except Exception as exc: # noqa: BLE001 @@ -446,27 +683,47 @@ def _query(self, where: str, value: str) -> list[PatientRecord]: except Exception: # noqa: BLE001 pass self.pages_read += 1 - return [ - PatientRecord( - str(pid), - str(f or ""), - str(lname or ""), - str(d or "")[:10], - str(mrn or ""), + out = [] + for pid, f, ln, d, mrn, phone, street, email, bf, bl in rows: + prior = ((str(bf or f or ""), str(bl)),) if bl else () + out.append( + PatientRecord( + str(pid), + str(f or ""), + str(ln or ""), + str(d or "")[:10], + str(mrn or ""), + str(phone or ""), + str(street or ""), + str(email or ""), + prior, + ) ) - for pid, f, lname, d, mrn in rows - ] + return out def by_birthdate(self, dob: str) -> list[PatientRecord]: return self._query("DOB = ?", dob) def by_family(self, family: str) -> list[PatientRecord]: # FHIR's string search is a case-insensitive prefix; match it. - return self._query("LOWER(lname) LIKE LOWER(?) || '%'", family) + return self._query( + "LOWER(lname) LIKE LOWER(?) || '%' OR LOWER(birth_lname) LIKE LOWER(?) || '%'", + family, + family, + ) def by_identifier(self, mrn: str) -> list[PatientRecord]: return self._query("pubpid = ?", mrn) + def by_given(self, given: str) -> list[PatientRecord]: + return self._query("LOWER(fname) = LOWER(?)", given) + + def by_phone(self, phone: str) -> list[PatientRecord]: + return self._query("phone_home = ?", phone) + + def by_email(self, email: str) -> list[PatientRecord]: + return self._query("LOWER(email) = LOWER(?)", email) + class TableSource: """An in-memory patient table with FHIR search semantics (offline use).""" @@ -484,12 +741,34 @@ def by_birthdate(self, dob: str) -> list[PatientRecord]: def by_family(self, family: str) -> list[PatientRecord]: self.pages_read += 1 prefix = norm_name(family) - return [r for r in self.records if norm_name(r.lname).startswith(prefix)] + return [ + r + for r in self.records + if any( + norm_name(last).startswith(prefix) + for last in (r.lname, *(n[1] for n in r.prior_names)) + ) + ] def by_identifier(self, mrn: str) -> list[PatientRecord]: self.pages_read += 1 return [r for r in self.records if r.mrn and r.mrn == mrn] + def by_given(self, given: str) -> list[PatientRecord]: + self.pages_read += 1 + prefix = norm_name(given) + return [r for r in self.records if norm_name(r.fname).startswith(prefix)] + + def by_phone(self, phone: str) -> list[PatientRecord]: + self.pages_read += 1 + return [ + r for r in self.records if r.phone and _digits(r.phone) == _digits(phone) + ] + + def by_email(self, email: str) -> list[PatientRecord]: + self.pages_read += 1 + return [r for r in self.records if r.email.casefold() == email.casefold()] + class RestPatientSource: """OpenEMR's standard REST API: ``GET .../api/patient?DOB=...``. @@ -562,6 +841,21 @@ def search(self, params: dict[str, str]) -> list[PatientRecord]: lname=str(row.get("lname") or ""), dob=str(row.get("DOB") or "")[:10], mrn=str(row.get("pubpid") or ""), + phone=str(row.get("phone_home") or ""), + street=str(row.get("street") or ""), + email=str(row.get("email") or ""), + prior_names=( + ( + ( + str( + row.get("birth_fname") or row.get("fname") or "" + ), + str(row["birth_lname"]), + ), + ) + if row.get("birth_lname") + else () + ), ) ) links = body.get("links") if isinstance(body, dict) else None @@ -588,6 +882,15 @@ def by_family(self, family: str) -> list[PatientRecord]: def by_identifier(self, mrn: str) -> list[PatientRecord]: return self.search({"pubpid": mrn}) + def by_given(self, given: str) -> list[PatientRecord]: + return self.search({"fname": given}) + + def by_phone(self, phone: str) -> list[PatientRecord]: + return self.search({"phone_home": phone}) + + def by_email(self, email: str) -> list[PatientRecord]: + return self.search({"email": email}) + def source_for_verifier(verifier: Any) -> Optional[PatientSource]: """The patient search on the same channel as a deployment's record check. diff --git a/packages/flow/openadapt_flow/runtime/replayer.py b/packages/flow/openadapt_flow/runtime/replayer.py index a9b4da3aa..5a8438b3b 100644 --- a/packages/flow/openadapt_flow/runtime/replayer.py +++ b/packages/flow/openadapt_flow/runtime/replayer.py @@ -17791,7 +17791,7 @@ def _no_duplicate_record(self, source: Any, params: dict[str, Any]) -> bool: want = { name: runtime_param_text(params[name]) if name in params else "" - for name in ("fname", "lname", "DOB", "mrn") + for name in ("fname", "lname", "DOB", "mrn", "phone_home", "email", "street") } try: result = check_duplicate(source, want) diff --git a/packages/flow/tests/test_duplicate_record_check.py b/packages/flow/tests/test_duplicate_record_check.py index 28575c849..218ed62a2 100644 --- a/packages/flow/tests/test_duplicate_record_check.py +++ b/packages/flow/tests/test_duplicate_record_check.py @@ -237,17 +237,25 @@ def test_sql_fallback(tmp_path: Path) -> None: db = tmp_path / "openemr.db" con = sqlite3.connect(db) con.execute( - "CREATE TABLE patient_data (pid INTEGER, fname TEXT, lname TEXT, " - "DOB TEXT, pubpid TEXT)" + # OpenEMR's patient_data columns the fallback reads. + "CREATE TABLE patient_data (pid INTEGER, fname TEXT, lname TEXT, DOB TEXT, " + "pubpid TEXT, phone_home TEXT, street TEXT, email TEXT, birth_fname TEXT, " + "birth_lname TEXT)" ) con.execute( "INSERT INTO patient_data VALUES (4, 'Rosalind', 'Jesswood-Okafor', " - "'1990-04-09', '4')" + "'1990-04-09', '4', '', '', '', NULL, NULL)" + ) + # A chart stored under a married name, with the birth name kept. + con.execute( + "INSERT INTO patient_data VALUES (5, 'Rosalind', 'Okafor', '1990-04-10', " + "'5', '', '', '', 'Rosalind', 'Jesswood')" ) con.commit() con.close() result = check_duplicate(SqlPatientSource(lambda: sqlite3.connect(db)), WANT) assert result.decision == "close" and result.channel == "sql" + assert {c.record.record_id for c in result.candidates} == {"4", "5"} # -- in the replayer -------------------------------------------------------------- @@ -294,3 +302,88 @@ def by_birthdate(self, dob: str) -> list[PatientRecord]: passed, why = _replayer_check(Broken(), WANT) assert passed is False and "unreadable" in why + + +# -- round two: nicknames, swapped names, prior names, contact tie-breakers ------- + +FULL = { + **WANT, + "phone_home": "617-555-0142", + "email": "r.j@example.test", + "street": "41 Larch Way", +} + + +def full( + fname: str, + lname: str, + dob: str, + *, + phone: str = "", + email: str = "", + street: str = "", + prior: tuple = (), +) -> PatientRecord: + return PatientRecord("9", fname, lname, dob, "", phone, street, email, prior) + + +@pytest.mark.parametrize( + ("record", "match"), + [ + # A married name and a mistyped day, tied by the phone. + (full("Rosalind", "Okafor", "1990-04-10", phone="(617) 555-0142"), "close"), + # The same, with nothing to tie them: not a candidate. + (full("Rosalind", "Okafor", "1990-04-10"), None), + # A nickname. + (full("Roz", "Jesswood", "1990-04-09"), None), # not in the table + (full("Rosalind", "Jesswood", "1990-04-09"), "exact"), + # First and last name swapped. + (full("Jesswood", "Rosalind", "1990-04-09"), "close"), + # A prior (maiden) name on the chart, with a mistyped day. + ( + full("Rosalind", "Okafor", "1990-04-10", prior=(("Rosalind", "Jesswood"),)), + "close", + ), + # A full name change: the same birth date and the same email. + (full("Mary", "Okafor", "1990-04-09", email="R.J@example.test"), "close"), + # A full name change with a near birth date needs two contact details. + (full("Mary", "Okafor", "1990-04-10", email="r.j@example.test"), None), + ( + full( + "Mary", + "Okafor", + "1990-04-10", + email="r.j@example.test", + street="41 LARCH WAY", + ), + "close", + ), + ], +) +def test_round_two(record: PatientRecord, match: Optional[str]) -> None: + found = classify(FULL, record) + assert (found.match if found else None) == match, found + + +def test_nickname_table() -> None: + want = {"fname": "Bob", "lname": "Ashby", "DOB": "1950-02-03"} + assert classify(want, rec("Robert", "Ashby", "1950-02-03")).match == "close" + assert classify(want, rec("Roberta", "Ashby", "1971-02-03")) is None + + +def test_fhir_reads_old_names_and_contacts(serve) -> None: + resource = patient("12", "Rosalind", "Okafor", "1990-04-10") + resource["name"].append( + {"use": "maiden", "family": "Jesswood", "given": ["Rosalind"]} + ) + resource["telecom"] = [{"system": "phone", "use": "home", "value": "617-555-0142"}] + server = serve( + lambda p, q: ( + 200, + {"resourceType": "Bundle", "total": 1, "entry": [{"resource": resource}]}, + ) + ) + result = check_duplicate(FhirPatientSource(server.url), FULL) + assert result.decision == "close" + record = result.candidates[0].record + assert record.prior_names == (("Rosalind", "Jesswood"),) and record.phone From 6f9efcde7d16684c7f02121af30a03604d7515c7 Mon Sep 17 00:00:00 2001 From: abrichr Date: Sat, 24 Oct 2026 19:57:50 -0400 Subject: [PATCH 03/13] test: the record duplicate check's halt keeps the no_duplicate_candidate prefix Co-Authored-By: Claude Opus 5.5 --- packages/flow/tests/test_duplicate_record_check.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/flow/tests/test_duplicate_record_check.py b/packages/flow/tests/test_duplicate_record_check.py index 218ed62a2..bfca8b1e8 100644 --- a/packages/flow/tests/test_duplicate_record_check.py +++ b/packages/flow/tests/test_duplicate_record_check.py @@ -289,6 +289,8 @@ def test_the_replayer_asks_the_record_when_a_channel_is_configured() -> None: table.records.append(rec("Rosalind", "Okafor", "1990-04-09", "8")) passed, why = _replayer_check(table, WANT) assert passed is False + # #1517 classes "failed: no_duplicate_candidate" as an item-input stop. + assert why.startswith("no_duplicate_candidate ") assert "record check via table: close" in why assert "close record 8 (same date of birth and first name" in why From 9fce5520ca860b06fc08aa32288537006bcc55c8 Mon Sep 17 00:00:00 2001 From: abrichr Date: Sat, 24 Oct 2026 20:00:26 -0400 Subject: [PATCH 04/13] duplicate check: an initial matches its first name; plant production-lifecycle's six cases All six cases production-lifecycle named stop as close matches: a married name with a new address, a mistyped day, a second surname, an initial, a crossed name, and both names misspelled with the same contact. The initial was the one gap. Extended stream (676 items, 223 true duplicates): false stops 2 (0.3 per 100 items), misses 24, all name change plus move. Co-Authored-By: Claude Opus 5.5 --- CAPABILITIES.md | 2 +- llms-full.txt | 2 +- packages/flow/benchmark/dup_check/README.md | 57 +- packages/flow/benchmark/dup_check/measure.py | 70 + .../dup_check/results/seed7-500.json | 1372 +++++++++++----- .../dup_check/results/soak-seed7-55.json | 1462 ++++++++++++----- packages/flow/docs/DUPLICATE_CHECK.md | 2 +- .../runtime/checks/duplicate_record.py | 5 +- .../flow/tests/test_duplicate_record_check.py | 27 + 9 files changed, 2118 insertions(+), 881 deletions(-) diff --git a/CAPABILITIES.md b/CAPABILITIES.md index d6adc674d..3c92d7ab2 100644 --- a/CAPABILITIES.md +++ b/CAPABILITIES.md @@ -229,7 +229,7 @@ improvising or retrying when something is off. | Repair by default: once a repair agent is configured, every stop that wrote nothing goes to it without anyone starting it; a stop after a possible write goes to reconciliation; every stop is a row in a ledger with the funnel from stop to resumed item | On main ([#1360](https://github.com/OpenAdaptAI/OpenAdapt/pull/1360)) | Synthetic (MockMed, 22 drift modes, real Claude agent: 6 of 21 repairable stops resumed and verified, 0 wrong saves in 66 runs); routing over recorded OpenEMR stops; live openIMIS relabel: 3 of 3 reached repair, 0 resumed; silent-saves 1.1.0 UI-change blocks: 18 of 28 items that always stopped now correct, 0 wrong | `run` (on by default, `--no-auto-repair`), `repair:` in `deploy.yaml`, [benchmark/auto_repair](packages/flow/benchmark/auto_repair/README.md) | | Repair a relabeled save button: prove from the recorded identity hash that only the label changed (same control id, kind and form), re-qualify the fix on the new release, and have a person admit the certified copy afresh, since a save button's identity is part of the record check | On main | Real app (local OpenEMR, synthetic data): after the save button was relabeled, the stopped item re-ran and saved once after 8.3 operator minutes and 763 s of re-qualification; 896 s from stop to done ([#1513](https://github.com/OpenAdaptAI/OpenAdapt/pull/1513)) | `openadapt_flow/repair/relabel.py`, [SELF_REPAIR.md](packages/flow/docs/SELF_REPAIR.md#a-relabeled-control-proof-that-only-the-label-changed) | | A duplicate check that reads the app's match list (`no_duplicate_candidate`): stop only on a listed record with the item's last name or date of birth, fail closed on an unreadable or unloaded list | On main | Real app (local OpenEMR, synthetic data): false stops per 100 items 70.4 before, 16.4 after; 0 silent wrongs in 30 saves ([#1513](https://github.com/OpenAdaptAI/OpenAdapt/pull/1513)) | [DUPLICATE_CHECK.md](packages/flow/docs/DUPLICATE_CHECK.md) | -| A duplicate check that asks the system of record (FHIR `Patient` search or OpenEMR's patient API, every page; SQL as a documented fallback): stop on an exact or close match, proceed otherwise, fail closed on any read problem. The default when `effects` reads FHIR or OpenEMR's patient API | Open PR | Offline on the soak's seed-7 stream with planted true duplicates: false stops per 100 items 40.5 (screen) against 0.3 (record) on 622 items; true duplicates missed: screen 30, record 24 of 169, every record miss a name change plus a move that shares no name, contact or date with the chart. Not yet run on live OpenEMR | [DUPLICATE_CHECK.md](packages/flow/docs/DUPLICATE_CHECK.md) | +| A duplicate check that asks the system of record (FHIR `Patient` search or OpenEMR's patient API, every page; SQL as a documented fallback): stop on an exact or close match, proceed otherwise, fail closed on any read problem. The default when `effects` reads FHIR or OpenEMR's patient API | Open PR | Offline on the soak's seed-7 stream with planted true duplicates: false stops per 100 items 37.3 (screen) against 0.3 (record) on 676 items; true duplicates missed: screen 30, record 24 of 223, every record miss a name change plus a move that shares no name, contact or date with the chart. Not yet run on live OpenEMR | [DUPLICATE_CHECK.md](packages/flow/docs/DUPLICATE_CHECK.md) | | A switched-off `problems_only` gate asks a person before the save instead of stopping the item | On main | Synthetic; real app in [#1513](https://github.com/OpenAdaptAI/OpenAdapt/pull/1513) | [APPROVAL_MODES.md](packages/flow/docs/APPROVAL_MODES.md#when-the-mode-turns-itself-off) | | Fleet drift patches: a verified fix with no patient data, shared across sites under policy | Released, local reference hub | Simulator | `openadapt-flow fleet`, [FLEET_DRIFT_PATCHES.md](packages/flow/docs/FLEET_DRIFT_PATCHES.md) | diff --git a/llms-full.txt b/llms-full.txt index a43b25628..8fab2d684 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -626,7 +626,7 @@ improvising or retrying when something is off. | Repair by default: once a repair agent is configured, every stop that wrote nothing goes to it without anyone starting it; a stop after a possible write goes to reconciliation; every stop is a row in a ledger with the funnel from stop to resumed item | On main ([#1360](https://github.com/OpenAdaptAI/OpenAdapt/pull/1360)) | Synthetic (MockMed, 22 drift modes, real Claude agent: 6 of 21 repairable stops resumed and verified, 0 wrong saves in 66 runs); routing over recorded OpenEMR stops; live openIMIS relabel: 3 of 3 reached repair, 0 resumed; silent-saves 1.1.0 UI-change blocks: 18 of 28 items that always stopped now correct, 0 wrong | `run` (on by default, `--no-auto-repair`), `repair:` in `deploy.yaml`, [benchmark/auto_repair](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/benchmark/auto_repair/README.md) | | Repair a relabeled save button: prove from the recorded identity hash that only the label changed (same control id, kind and form), re-qualify the fix on the new release, and have a person admit the certified copy afresh, since a save button's identity is part of the record check | On main | Real app (local OpenEMR, synthetic data): after the save button was relabeled, the stopped item re-ran and saved once after 8.3 operator minutes and 763 s of re-qualification; 896 s from stop to done ([#1513](https://github.com/OpenAdaptAI/OpenAdapt/pull/1513)) | `openadapt_flow/repair/relabel.py`, [SELF_REPAIR.md](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/SELF_REPAIR.md#a-relabeled-control-proof-that-only-the-label-changed) | | A duplicate check that reads the app's match list (`no_duplicate_candidate`): stop only on a listed record with the item's last name or date of birth, fail closed on an unreadable or unloaded list | On main | Real app (local OpenEMR, synthetic data): false stops per 100 items 70.4 before, 16.4 after; 0 silent wrongs in 30 saves ([#1513](https://github.com/OpenAdaptAI/OpenAdapt/pull/1513)) | [DUPLICATE_CHECK.md](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/DUPLICATE_CHECK.md) | -| A duplicate check that asks the system of record (FHIR `Patient` search or OpenEMR's patient API, every page; SQL as a documented fallback): stop on an exact or close match, proceed otherwise, fail closed on any read problem. The default when `effects` reads FHIR or OpenEMR's patient API | Open PR | Offline on the soak's seed-7 stream with planted true duplicates: false stops per 100 items 40.5 (screen) against 0.3 (record) on 622 items; true duplicates missed: screen 30, record 24 of 169, every record miss a name change plus a move that shares no name, contact or date with the chart. Not yet run on live OpenEMR | [DUPLICATE_CHECK.md](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/DUPLICATE_CHECK.md) | +| A duplicate check that asks the system of record (FHIR `Patient` search or OpenEMR's patient API, every page; SQL as a documented fallback): stop on an exact or close match, proceed otherwise, fail closed on any read problem. The default when `effects` reads FHIR or OpenEMR's patient API | Open PR | Offline on the soak's seed-7 stream with planted true duplicates: false stops per 100 items 37.3 (screen) against 0.3 (record) on 676 items; true duplicates missed: screen 30, record 24 of 223, every record miss a name change plus a move that shares no name, contact or date with the chart. Not yet run on live OpenEMR | [DUPLICATE_CHECK.md](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/DUPLICATE_CHECK.md) | | A switched-off `problems_only` gate asks a person before the save instead of stopping the item | On main | Synthetic; real app in [#1513](https://github.com/OpenAdaptAI/OpenAdapt/pull/1513) | [APPROVAL_MODES.md](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/APPROVAL_MODES.md#when-the-mode-turns-itself-off) | | Fleet drift patches: a verified fix with no patient data, shared across sites under policy | Released, local reference hub | Simulator | `openadapt-flow fleet`, [FLEET_DRIFT_PATCHES.md](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/FLEET_DRIFT_PATCHES.md) | diff --git a/packages/flow/benchmark/dup_check/README.md b/packages/flow/benchmark/dup_check/README.md index 3148f3a81..0b8dfffd4 100644 --- a/packages/flow/benchmark/dup_check/README.md +++ b/packages/flow/benchmark/dup_check/README.md @@ -49,32 +49,53 @@ nothing with the chart but the person, so no rule here can catch them. The `r2_` rules were written knowing the `r2_` kinds, so those catches are not a blind test. -Extended stream (500 seed-7 items, plus the planted and extra charts): 622 -items, 440 new charts, 169 true duplicates. +Then production-lifecycle named six cases that must stop. Each is planted as +a `pl_` kind: +- a married name and a new address; +- a mistyped day of birth; +- a second surname ("Garcia Lopez" against "Garcia"); +- an initial ("J." for the first name); +- a crossed name; +- both names misspelled with the same phone and email. + +The initial was the one gap, and a rule was added for it. + +Extended stream (500 seed-7 items, plus the planted and extra charts): 676 +items, 440 new charts, 223 true duplicates. | check | false stops (per 100 items) | missed duplicates | |---|---:|---:| -| screen (`no_duplicate_candidate` on the list) | 252 (40.5) | 30 | -| record, first version | 2 (0.3) | 60 | -| record, second round | 2 (0.3) | 24 | +| screen (`no_duplicate_candidate` on the list) | 252 (37.3) | 30 | +| record, first version | 2 (0.3) | 72 | +| record, now | 2 (0.3) | 24 | -The soak's own 55 items, with the same planting (177 items, 130 true -duplicates): screen 10 false stops (5.6 per 100), 30 missed; record 0 false +The soak's own 55 items, with the same planting (231 items, 184 true +duplicates): screen 10 false stops (4.3 per 100), 30 missed; record 0 false stops, 24 missed. -Missed duplicates by kind, extended stream: +Every kind, extended stream (planted count; missed by screen, record v1, +record now): -| kind | planted | screen | record v1 | record v2 | field that would have caught it | +| kind | planted | screen | v1 | now | what catches it now | |---|---:|---:|---:|---:|---| -| married name + mistyped day (`heldout_married_dob_typo`) | 12 | 12 | 12 | 0 | phone, street, email (caught by v2) | -| first and last name swapped (`r2_name_swap`) | 12 | 0 | 12 | 0 | the names, crossed (v2) | -| nickname, Bob for Robert (`r2_nickname`) | 6 | 0 | 6 | 0 | the nickname table (v2) | -| prior name on the chart, moved, mistyped day (`r2_prior_name_moved_dob_typo`) | 6 | 6 | 6 | 0 | the chart's birth name (v2) | -| year digits transposed (`r2_year_transposed`) | 11 | 0 | 0 | 0 | the date of birth, one field off | -| married name, moved, mistyped day (`limit_married_moved_dob_typo`) | 12 | 12 | 12 | 12 | none in the referral: needs an MRN, an insurance id or a person | -| new first and last name, moved, same day (`limit_new_name_moved_same_dob`) | 12 | 0 | 12 | 12 | none in the referral; the screen stops it only because it stops every shared date of birth | - -**The trade-off.** Misses fell from 60 to 24, and every remaining miss is a +| married name + mistyped day (`heldout_married_dob_typo`) | 12 | 12 | 12 | 0 | first name, a near date of birth, a shared phone, street or email | +| married name, new address (`pl_married_moved`) | 12 | 0 | 0 | 0 | same first name and date of birth | +| same name, mistyped day (`pl_ambiguous_same_person`, `ambiguous_same_person`) | 22 | 0 | 0 | 0 | same name, date of birth one field off | +| day and month swapped (`planted_swapped_dob`) | 3 | 0 | 0 | 0 | same name, swapped date | +| second surname (`pl_second_surname`) | 6 | 0 | 0 | 0 | a shared surname part, same first name and date of birth | +| hyphenated name (`planted_hyphenated`) | 12 | 0 | 0 | 0 | a shared surname part | +| initial for the first name (`pl_initial`) | 12 | 0 | 12 | 0 | the initial rule | +| nickname (`r2_nickname`) | 6 | 0 | 6 | 0 | the nickname table | +| first and last name swapped (`r2_name_swap`) | 12 | 0 | 12 | 0 | the crossed names | +| both names misspelled, same contact (`pl_both_typos_same_contact`) | 12 | 0 | 0 | 0 | similar names, same date of birth | +| prior name on the chart, moved, mistyped day (`r2_prior_name_moved_dob_typo`) | 6 | 6 | 6 | 0 | the chart's birth name | +| year digits transposed (`r2_year_transposed`) | 11 | 0 | 0 | 0 | same name, date of birth one field off | +| married name, moved, mistyped day (`limit_married_moved_dob_typo`) | 12 | 12 | 12 | 12 | nothing in the referral: needs an MRN, an insurance id or a person | +| new first and last name, moved, same day (`limit_new_name_moved_same_dob`) | 12 | 0 | 12 | 12 | nothing in the referral; the screen stops it only because it stops every shared date of birth | + +Every match the record check makes is `close` or `exact`, and both stop. + +**The trade-off.** Misses fell from 72 to 24, and every remaining miss is a `limit_` kind, while false stops stayed at 2 of 440 new charts. Both are `shared_dob` items whose first name equals that of the chart with the same birth date, which the married-name rule sends to a person on purpose. The diff --git a/packages/flow/benchmark/dup_check/measure.py b/packages/flow/benchmark/dup_check/measure.py index 6892875b0..3c1612556 100644 --- a/packages/flow/benchmark/dup_check/measure.py +++ b/packages/flow/benchmark/dup_check/measure.py @@ -62,6 +62,16 @@ "r2_name_swap", "r2_year_transposed", "r2_prior_name_moved_dob_typo", + "pl_married_moved", + "pl_second_surname", + "pl_initial", + "pl_both_typos_same_contact", + "pl_ambiguous_same_person", + "pl_married_moved", + "pl_second_surname", + "pl_initial", + "pl_both_typos_same_contact", + "pl_ambiguous_same_person", "limit_married_moved_dob_typo", "limit_new_name_moved_same_dob", } @@ -233,6 +243,46 @@ def round_two(n: int, person: dict[str, str]) -> list[Item]: [], ) ) + + # The six cases production-lifecycle named (``pl_``). + def typo(v: str) -> str: + return v[:-2] + v[-1] + v[-2] if len(v) > 3 else v + "e" + + out += [ + Item( + f"pl-mm-{n}", + "pl_married_moved", + dict(moved(person, n), lname=MARRIED[(n + 4) % len(MARRIED)]), + None, + [], + ), + Item( + f"pl-in-{n}", + "pl_initial", + dict(person, fname=person["fname"][0] + "."), + None, + [], + ), + Item( + f"pl-bt-{n}", + "pl_both_typos_same_contact", + dict( + person, + fname=typo(person["fname"]), + lname=typo(person["lname"]), + street=f"{n} SOMEWHERE ELSE", + ), + None, + [], + ), + Item( + f"pl-as-{n}", + "pl_ambiguous_same_person", + dict(person, DOB=day_typo(person["DOB"])), + None, + [], + ), + ] out.append( Item( f"lim-mm-{n}", @@ -279,6 +329,26 @@ def extra_existing( items.append( Item(f"r2-nk-{n}", "r2_nickname", dict(chart, fname=nick), None, []) ) + for n in range(6): + base = existing[(n + 5) % len(existing)] + chart = dict( + base, + fname=f"Second{n}", + lname=f"Garcia{n} Lopez{n}", + email=f"second.{n}@example.invalid", + phone_home=f"555-05{n:02d}", + street=f"{600 + n} SECOND STREET", + ) + extra.append(chart) + items.append( + Item( + f"pl-ss-{n}", + "pl_second_surname", + dict(moved(chart, 70 + n), lname=f"Garcia{n}"), + None, + [], + ) + ) for n in range(6): base = existing[(n + 3) % len(existing)] chart = dict( diff --git a/packages/flow/benchmark/dup_check/results/seed7-500.json b/packages/flow/benchmark/dup_check/results/seed7-500.json index c377d3749..5211933d5 100644 --- a/packages/flow/benchmark/dup_check/results/seed7-500.json +++ b/packages/flow/benchmark/dup_check/results/seed7-500.json @@ -32,6 +32,16 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "pl_ambiguous_same_person", + "item": "pl-as-9", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "shared_dob", "item": "ref-00005", @@ -98,6 +108,16 @@ "right": "no chart: duplicate", "screen": "stop" }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-9", + "record": "close", + "record_why": [ + "same first name, another last name, same phone, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, { "exception": "concurrent_other", "item": "ref-00013", @@ -189,14 +209,15 @@ "screen": "proceed" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-9", + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-6", "record": "close", "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "ambiguous_same_person", @@ -325,11 +346,11 @@ "screen": "stop" }, { - "exception": "planted_hyphenated", - "item": "plant-h-1", + "exception": "pl_ambiguous_same_person", + "item": "pl-as-0", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -343,12 +364,12 @@ "screen": "proceed" }, { - "exception": "heldout_first_typo", - "item": "held-f-7", + "exception": "planted_married", + "item": "plant-m-5", "record": "close", "record_why": [ "same date of birth and first name, another last name", - "same date of birth, a similar name" + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" @@ -415,12 +436,12 @@ "screen": "proceed" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-2", + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-1", "record": "none", "record_why": [], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "clean", @@ -447,11 +468,21 @@ "screen": "proceed" }, { - "exception": "r2_name_swap", - "item": "r2-sw-2", + "exception": "pl_married_moved", + "item": "pl-mm-10", "record": "close", "record_why": [ - "first and last name swapped" + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "pl_ambiguous_same_person", + "item": "pl-as-1", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -510,10 +541,21 @@ "screen": "stop" }, { - "exception": "planted_hyphenated", - "item": "plant-h-10", + "exception": "r2_name_swap", + "item": "r2-sw-11", + "record": "close", + "record_why": [ + "first and last name swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-7", "record": "close", "record_why": [ + "same date of birth and first name, another last name", "same date of birth, a similar name" ], "right": "no chart: duplicate", @@ -544,18 +586,18 @@ "screen": "proceed" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-8", + "exception": "pl_initial", + "item": "pl-in-11", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_hyphenated", - "item": "plant-h-4", + "exception": "heldout_first_typo", + "item": "held-f-6", "record": "close", "record_why": [ "same date of birth and first name, another last name", @@ -564,6 +606,14 @@ "right": "no chart: duplicate", "screen": "stop" }, + { + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-2", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "failed_save", "item": "ref-00058", @@ -573,44 +623,44 @@ "screen": "proceed" }, { - "exception": "planted_married", - "item": "plant-m-2", + "exception": "r2_name_swap", + "item": "r2-sw-1", "record": "close", "record_why": [ - "same date of birth and first name, another last name" + "first and last name swapped" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-0", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "r2_name_swap", - "item": "r2-sw-8", + "exception": "pl_married_moved", + "item": "pl-mm-0", "record": "close", "record_why": [ - "first and last name swapped" + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_married", - "item": "plant-m-4", + "exception": "planted_hyphenated", + "item": "plant-h-6", "record": "close", "record_why": [ "same date of birth and first name, another last name", - "same date of birth and first name, another last name" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, + { + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-2", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, { "exception": "clean", "item": "ref-00059", @@ -620,8 +670,8 @@ "screen": "proceed" }, { - "exception": "heldout_last_typo", - "item": "held-l-2", + "exception": "pl_initial", + "item": "pl-in-1", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -638,12 +688,11 @@ "screen": "proceed" }, { - "exception": "planted_hyphenated", - "item": "plant-h-8", + "exception": "pl_ambiguous_same_person", + "item": "pl-as-5", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -692,15 +741,14 @@ "screen": "proceed" }, { - "exception": "heldout_first_typo", - "item": "held-f-6", + "exception": "heldout_married_dob_typo", + "item": "held-md-4", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" + "same first name, another last name, same phone, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "clean", @@ -711,10 +759,12 @@ "screen": "proceed" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-10", - "record": "none", - "record_why": [], + "exception": "pl_ambiguous_same_person", + "item": "pl-as-7", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth" + ], "right": "no chart: duplicate", "screen": "stop" }, @@ -726,6 +776,14 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-8", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00068", @@ -735,8 +793,8 @@ "screen": "proceed" }, { - "exception": "heldout_last_typo", - "item": "held-l-7", + "exception": "planted_hyphenated", + "item": "plant-h-5", "record": "close", "record_why": [ "same date of birth and first name, another last name", @@ -746,11 +804,11 @@ "screen": "stop" }, { - "exception": "r2_name_swap", - "item": "r2-sw-1", + "exception": "planted_hyphenated", + "item": "plant-h-1", "record": "close", "record_why": [ - "first and last name swapped" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -798,12 +856,10 @@ "screen": "proceed" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-5", - "record": "close", - "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" - ], + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-3", + "record": "none", + "record_why": [], "right": "no chart: duplicate", "screen": "proceed" }, @@ -866,15 +922,12 @@ "screen": "proceed" }, { - "exception": "heldout_first_typo", - "item": "held-f-8", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-5", + "record": "none", + "record_why": [], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "common_surname", @@ -884,6 +937,16 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "planted_hyphenated", + "item": "plant-h-11", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00081", @@ -910,6 +973,14 @@ "right": "no chart: duplicate", "screen": "stop" }, + { + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-11", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00084", @@ -919,14 +990,12 @@ "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-1", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-0", + "record": "none", + "record_why": [], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "clean", @@ -947,12 +1016,14 @@ "screen": "stop" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-1", - "record": "none", - "record_why": [], + "exception": "heldout_last_typo", + "item": "held-l-1", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "clean", @@ -973,10 +1044,12 @@ "screen": "stop" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-3", - "record": "none", - "record_why": [], + "exception": "pl_ambiguous_same_person", + "item": "pl-as-2", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth" + ], "right": "no chart: duplicate", "screen": "stop" }, @@ -1031,11 +1104,11 @@ "screen": "stop" }, { - "exception": "r2_name_swap", - "item": "r2-sw-3", + "exception": "pl_married_moved", + "item": "pl-mm-2", "record": "close", "record_why": [ - "first and last name swapped" + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" @@ -1051,10 +1124,13 @@ "screen": "stop" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-9", - "record": "none", - "record_why": [], + "exception": "planted_married", + "item": "plant-m-7", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" + ], "right": "no chart: duplicate", "screen": "stop" }, @@ -1084,6 +1160,16 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-10", + "record": "close", + "record_why": [ + "same first name, another last name, same phone, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, { "exception": "clean", "item": "ref-00098", @@ -1125,12 +1211,11 @@ "screen": "proceed" }, { - "exception": "heldout_last_typo", - "item": "held-l-6", + "exception": "r2_name_swap", + "item": "r2-sw-4", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" + "first and last name swapped" ], "right": "no chart: duplicate", "screen": "stop" @@ -1152,11 +1237,11 @@ "screen": "proceed" }, { - "exception": "heldout_last_typo", - "item": "held-l-10", + "exception": "r2_name_swap", + "item": "r2-sw-7", "record": "close", "record_why": [ - "same date of birth, a similar name" + "first and last name swapped" ], "right": "no chart: duplicate", "screen": "stop" @@ -1194,15 +1279,14 @@ "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-3", + "exception": "heldout_married_dob_typo", + "item": "held-md-2", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" + "same first name, another last name, same phone, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "dup_resend", @@ -1215,11 +1299,12 @@ "screen": "stop" }, { - "exception": "r2_name_swap", - "item": "r2-sw-11", + "exception": "heldout_last_typo", + "item": "held-l-8", "record": "close", "record_why": [ - "first and last name swapped" + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -1266,6 +1351,14 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-9", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, { "exception": "ambiguous_same_person", "item": "ref-00115", @@ -1293,11 +1386,11 @@ "screen": "proceed" }, { - "exception": "planted_hyphenated", - "item": "plant-h-2", + "exception": "r2_year_transposed", + "item": "r2-ty-1", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -1326,6 +1419,16 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "planted_hyphenated", + "item": "plant-h-9", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00121", @@ -1334,6 +1437,14 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-8", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, { "exception": "clean", "item": "ref-00122", @@ -1342,6 +1453,14 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-11", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, { "exception": "clean", "item": "ref-00123", @@ -1351,10 +1470,12 @@ "screen": "proceed" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-2", - "record": "none", - "record_why": [], + "exception": "planted_married", + "item": "plant-m-2", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], "right": "no chart: duplicate", "screen": "stop" }, @@ -1441,19 +1562,21 @@ "screen": "stop" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-6", - "record": "none", - "record_why": [], + "exception": "pl_ambiguous_same_person", + "item": "pl-as-4", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth" + ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_swapped_dob", - "item": "plant-s-3", + "exception": "heldout_last_typo", + "item": "held-l-2", "record": "close", "record_why": [ - "same name, day and month swapped" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -1475,14 +1598,12 @@ "screen": "proceed" }, { - "exception": "planted_married", - "item": "plant-m-11", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-7", + "record": "none", + "record_why": [], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "clean", @@ -1533,12 +1654,22 @@ "screen": "proceed" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-9", + "exception": "planted_married", + "item": "plant-m-9", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-6", "record": "none", "record_why": [], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "slow_save", @@ -1556,6 +1687,16 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "pl_married_moved", + "item": "pl-mm-9", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00144", @@ -1573,11 +1714,22 @@ "screen": "proceed" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-6", + "exception": "pl_married_moved", + "item": "pl-mm-11", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "pl_initial", + "item": "pl-in-4", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -1708,6 +1860,16 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-11", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00160", @@ -1741,8 +1903,8 @@ "screen": "proceed" }, { - "exception": "heldout_first_typo", - "item": "held-f-5", + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-3", "record": "close", "record_why": [ "same date of birth and first name, another last name", @@ -1792,14 +1954,14 @@ "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-3", + "exception": "r2_year_transposed", + "item": "r2-ty-2", "record": "close", "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "ambiguous_same_person", @@ -1848,11 +2010,11 @@ "screen": "stop" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-3", + "exception": "pl_initial", + "item": "pl-in-2", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -1946,11 +2108,12 @@ "screen": "stop" }, { - "exception": "planted_swapped_dob", - "item": "plant-s-10", + "exception": "heldout_last_typo", + "item": "held-l-7", "record": "close", "record_why": [ - "same name, day and month swapped" + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -1982,11 +2145,12 @@ "screen": "proceed" }, { - "exception": "planted_swapped_dob", - "item": "plant-s-4", + "exception": "planted_married", + "item": "plant-m-3", "record": "close", "record_why": [ - "same name, day and month swapped" + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" @@ -1999,6 +2163,26 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "heldout_first_typo", + "item": "held-f-9", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-9", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00189", @@ -2023,6 +2207,16 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "pl_ambiguous_same_person", + "item": "pl-as-8", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00192", @@ -2188,14 +2382,14 @@ "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-10", + "exception": "r2_year_transposed", + "item": "r2-ty-7", "record": "close", "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "clean", @@ -2214,11 +2408,19 @@ "screen": "stop" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-2", + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-1", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "pl_initial", + "item": "pl-in-9", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -2232,11 +2434,11 @@ "screen": "stop" }, { - "exception": "planted_married", - "item": "plant-m-1", + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-0", "record": "close", "record_why": [ - "same date of birth and first name, another last name" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -2258,11 +2460,11 @@ "screen": "proceed" }, { - "exception": "heldout_first_typo", - "item": "held-f-2", + "exception": "pl_married_moved", + "item": "pl-mm-1", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" @@ -2308,12 +2510,11 @@ "screen": "proceed" }, { - "exception": "heldout_last_typo", - "item": "held-l-5", + "exception": "pl_ambiguous_same_person", + "item": "pl-as-3", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -2367,8 +2568,8 @@ "screen": "stop" }, { - "exception": "planted_hyphenated", - "item": "plant-h-5", + "exception": "pl_initial", + "item": "pl-in-3", "record": "close", "record_why": [ "same date of birth and first name, another last name", @@ -2377,6 +2578,16 @@ "right": "no chart: duplicate", "screen": "stop" }, + { + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-9", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "dup_existing", "item": "ref-00228", @@ -2439,14 +2650,25 @@ "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-11", + "exception": "r2_year_transposed", + "item": "r2-ty-8", "record": "close", "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-8", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" }, { "exception": "malformed_case", @@ -2473,22 +2695,19 @@ "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-8", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-5", + "record": "none", + "record_why": [], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-1", + "exception": "heldout_first_typo", + "item": "held-f-1", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -2568,14 +2787,14 @@ "screen": "proceed" }, { - "exception": "heldout_first_typo", - "item": "held-f-10", + "exception": "heldout_married_dob_typo", + "item": "held-md-7", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same first name, another last name, same phone, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "concurrent_other", @@ -2594,11 +2813,11 @@ "screen": "stop" }, { - "exception": "planted_hyphenated", - "item": "plant-h-9", + "exception": "r2_year_transposed", + "item": "r2-ty-6", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -2667,6 +2886,16 @@ "right": "no chart", "screen": "stop" }, + { + "exception": "heldout_last_typo", + "item": "held-l-10", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00257", @@ -2716,8 +2945,8 @@ "screen": "proceed" }, { - "exception": "planted_hyphenated", - "item": "plant-h-6", + "exception": "heldout_last_typo", + "item": "held-l-4", "record": "close", "record_why": [ "same date of birth and first name, another last name", @@ -2815,12 +3044,15 @@ "screen": "stop" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-8", - "record": "none", - "record_why": [], + "exception": "heldout_last_typo", + "item": "held-l-6", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "ambiguous_new_person", @@ -2839,12 +3071,10 @@ "screen": "stop" }, { - "exception": "r2_name_swap", - "item": "r2-sw-5", - "record": "close", - "record_why": [ - "first and last name swapped" - ], + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-3", + "record": "none", + "record_why": [], "right": "no chart: duplicate", "screen": "stop" }, @@ -2865,19 +3095,18 @@ "screen": "stop" }, { - "exception": "planted_married", - "item": "plant-m-3", + "exception": "planted_hyphenated", + "item": "plant-h-2", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_married", - "item": "plant-m-5", + "exception": "pl_married_moved", + "item": "pl-mm-3", "record": "close", "record_why": [ "same date of birth and first name, another last name", @@ -2903,14 +3132,14 @@ "screen": "stop" }, { - "exception": "r2_name_swap", - "item": "r2-sw-7", + "exception": "heldout_married_dob_typo", + "item": "held-md-5", "record": "close", "record_why": [ - "first and last name swapped" + "same first name, another last name, same phone, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "missing_optional", @@ -3017,11 +3246,12 @@ "screen": "stop" }, { - "exception": "r2_name_swap", - "item": "r2-sw-4", + "exception": "heldout_last_typo", + "item": "held-l-3", "record": "close", "record_why": [ - "first and last name swapped" + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -3037,12 +3267,11 @@ "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-3", + "exception": "r2_name_swap", + "item": "r2-sw-2", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" + "first and last name swapped" ], "right": "no chart: duplicate", "screen": "stop" @@ -3108,6 +3337,16 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "planted_hyphenated", + "item": "plant-h-10", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "ambiguous_same_person", "item": "ref-00301", @@ -3126,6 +3365,17 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "pl_initial", + "item": "pl-in-8", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00303", @@ -3135,21 +3385,22 @@ "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-9", + "exception": "pl_initial", + "item": "pl-in-6", "record": "close", "record_why": [ + "same date of birth and first name, another last name", "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-7", + "exception": "r2_name_swap", + "item": "r2-sw-5", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "first and last name swapped" ], "right": "no chart: duplicate", "screen": "stop" @@ -3195,14 +3446,12 @@ "screen": "stop" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-9", - "record": "close", - "record_why": [ - "same name, a mistyped date of birth" - ], + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-6", + "record": "none", + "record_why": [], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "clean", @@ -3213,12 +3462,15 @@ "screen": "stop" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-6", - "record": "none", - "record_why": [], + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-4", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "concurrent_other", @@ -3237,12 +3489,14 @@ "screen": "proceed" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-7", - "record": "none", - "record_why": [], + "exception": "r2_year_transposed", + "item": "r2-ty-5", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth" + ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "clean", @@ -3260,6 +3514,16 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "pl_ambiguous_same_person", + "item": "pl-as-10", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "failed_save", "item": "ref-00314", @@ -3269,10 +3533,22 @@ "screen": "proceed" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-5", - "record": "none", - "record_why": [], + "exception": "planted_married", + "item": "plant-m-11", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_swapped_dob", + "item": "plant-s-4", + "record": "close", + "record_why": [ + "same name, day and month swapped" + ], "right": "no chart: duplicate", "screen": "stop" }, @@ -3300,6 +3576,43 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-10", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-11", + "record": "close", + "record_why": [ + "same first name, another last name, same phone, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-10", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-8", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "shared_dob", "item": "ref-00318", @@ -3441,11 +3754,12 @@ "screen": "proceed" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-10", + "exception": "pl_initial", + "item": "pl-in-7", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -3491,39 +3805,52 @@ "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-9", + "exception": "pl_married_moved", + "item": "pl-mm-6", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-4", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_name_swap", - "item": "r2-sw-10", + "exception": "r2_year_transposed", + "item": "r2-ty-3", "record": "close", "record_why": [ - "first and last name swapped" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-11", + "exception": "pl_married_moved", + "item": "pl-mm-7", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-9", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_married", + "item": "plant-m-8", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" @@ -3576,6 +3903,16 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "pl_ambiguous_same_person", + "item": "pl-as-11", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "common_surname", "item": "ref-00345", @@ -3608,6 +3945,17 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "pl_married_moved", + "item": "pl-mm-8", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "missing_optional", "item": "ref-00349", @@ -3657,12 +4005,14 @@ "screen": "stop" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-3", - "record": "none", - "record_why": [], + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-2", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "clean", @@ -3739,10 +4089,12 @@ "screen": "proceed" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-11", - "record": "none", - "record_why": [], + "exception": "heldout_married_dob_typo", + "item": "held-md-8", + "record": "close", + "record_why": [ + "same first name, another last name, same phone, a mistyped date of birth" + ], "right": "no chart: duplicate", "screen": "proceed" }, @@ -3778,6 +4130,16 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "pl_initial", + "item": "pl-in-10", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00368", @@ -3917,32 +4279,38 @@ "screen": "stop" }, { - "exception": "planted_hyphenated", - "item": "plant-h-7", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-4", + "record": "none", + "record_why": [], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_hyphenated", - "item": "plant-h-11", + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-7", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "pl_married_moved", + "item": "pl-mm-4", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "r2_name_swap", - "item": "r2-sw-6", + "exception": "heldout_first_typo", + "item": "held-f-11", "record": "close", "record_why": [ - "first and last name swapped" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -3996,14 +4364,14 @@ "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-2", + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-1", "record": "close", "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" + "same date of birth, a similar name" ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "clean", @@ -4046,11 +4414,11 @@ "screen": "stop" }, { - "exception": "planted_married", - "item": "plant-m-9", + "exception": "r2_name_swap", + "item": "r2-sw-6", "record": "close", "record_why": [ - "same date of birth and first name, another last name" + "first and last name swapped" ], "right": "no chart: duplicate", "screen": "stop" @@ -4103,6 +4471,16 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "r2_name_swap", + "item": "r2-sw-9", + "record": "close", + "record_why": [ + "first and last name swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "common_surname", "item": "ref-00401", @@ -4133,14 +4511,15 @@ "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-4", + "exception": "heldout_first_typo", + "item": "held-f-3", "record": "close", "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "missing_required", @@ -4151,11 +4530,11 @@ "screen": "stop" }, { - "exception": "r2_name_swap", - "item": "r2-sw-9", + "exception": "pl_ambiguous_same_person", + "item": "pl-as-6", "record": "close", "record_why": [ - "first and last name swapped" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -4179,11 +4558,10 @@ "screen": "stop" }, { - "exception": "planted_hyphenated", - "item": "plant-h-3", + "exception": "heldout_first_typo", + "item": "held-f-2", "record": "close", "record_why": [ - "same date of birth and first name, another last name", "same date of birth, a similar name" ], "right": "no chart: duplicate", @@ -4214,12 +4592,12 @@ "screen": "stop" }, { - "exception": "planted_married", - "item": "plant-m-8", + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-5", "record": "close", "record_why": [ "same date of birth and first name, another last name", - "same date of birth and first name, another last name" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -4265,12 +4643,12 @@ "screen": "stop" }, { - "exception": "planted_married", - "item": "plant-m-6", + "exception": "heldout_first_typo", + "item": "held-f-4", "record": "close", "record_why": [ "same date of birth and first name, another last name", - "same date of birth and first name, another last name" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -4291,6 +4669,16 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "r2_name_swap", + "item": "r2-sw-10", + "record": "close", + "record_why": [ + "first and last name swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00416", @@ -4308,14 +4696,14 @@ "screen": "proceed" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-1", + "exception": "planted_married", + "item": "plant-m-1", "record": "close", "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "clean", @@ -4333,17 +4721,9 @@ "right": "new chart", "screen": "stop" }, - { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-1", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "stop" - }, { "exception": "heldout_married_dob_typo", - "item": "held-md-8", + "item": "held-md-1", "record": "close", "record_why": [ "same first name, another last name, same phone, a mistyped date of birth" @@ -4351,6 +4731,17 @@ "right": "no chart: duplicate", "screen": "proceed" }, + { + "exception": "planted_married", + "item": "plant-m-6", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00420", @@ -4378,12 +4769,15 @@ "screen": "proceed" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-10", - "record": "none", - "record_why": [], + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-7", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "clean", @@ -4404,12 +4798,14 @@ "screen": "stop" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-4", - "record": "none", - "record_why": [], + "exception": "r2_name_swap", + "item": "r2-sw-3", + "record": "close", + "record_why": [ + "first and last name swapped" + ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "clean", @@ -4420,11 +4816,12 @@ "screen": "stop" }, { - "exception": "planted_married", - "item": "plant-m-10", + "exception": "planted_hyphenated", + "item": "plant-h-7", "record": "close", "record_why": [ - "same date of birth and first name, another last name" + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -4491,6 +4888,16 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "planted_married", + "item": "plant-m-10", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "shared_dob", "item": "ref-00433", @@ -4500,12 +4907,10 @@ "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-1", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-0", + "record": "none", + "record_why": [], "right": "no chart: duplicate", "screen": "stop" }, @@ -4526,14 +4931,14 @@ "screen": "proceed" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-4", + "exception": "heldout_married_dob_typo", + "item": "held-md-3", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same first name, another last name, same phone, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "clean", @@ -4586,15 +4991,12 @@ "screen": "stop" }, { - "exception": "planted_married", - "item": "plant-m-7", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" - ], + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-4", + "record": "none", + "record_why": [], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "clean", @@ -4605,10 +5007,13 @@ "screen": "stop" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-7", - "record": "none", - "record_why": [], + "exception": "pl_married_moved", + "item": "pl-mm-5", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" + ], "right": "no chart: duplicate", "screen": "stop" }, @@ -4644,6 +5049,16 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-10", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00447", @@ -4727,12 +5142,15 @@ "screen": "stop" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-5", - "record": "none", - "record_why": [], + "exception": "planted_hyphenated", + "item": "plant-h-4", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "clean", @@ -4742,6 +5160,16 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "heldout_first_typo", + "item": "held-f-10", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "malformed_case", "item": "ref-00458", @@ -4783,10 +5211,12 @@ "screen": "stop" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-11", - "record": "none", - "record_why": [], + "exception": "r2_name_swap", + "item": "r2-sw-8", + "record": "close", + "record_why": [ + "first and last name swapped" + ], "right": "no chart: duplicate", "screen": "stop" }, @@ -4807,10 +5237,11 @@ "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-11", + "exception": "planted_hyphenated", + "item": "plant-h-8", "record": "close", "record_why": [ + "same date of birth and first name, another last name", "same date of birth, a similar name" ], "right": "no chart: duplicate", @@ -4851,14 +5282,14 @@ "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-6", + "exception": "r2_year_transposed", + "item": "r2-ty-4", "record": "close", "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "clean", @@ -4869,8 +5300,8 @@ "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-4", + "exception": "planted_hyphenated", + "item": "plant-h-3", "record": "close", "record_why": [ "same date of birth and first name, another last name", @@ -5015,6 +5446,16 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "r2_year_transposed", + "item": "r2-ty-9", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00486", @@ -5024,11 +5465,12 @@ "screen": "stop" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-5", + "exception": "planted_married", + "item": "plant-m-4", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" @@ -5113,6 +5555,16 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "planted_swapped_dob", + "item": "plant-s-10", + "record": "close", + "record_why": [ + "same name, day and month swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00497", @@ -5137,6 +5589,16 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "r2_year_transposed", + "item": "r2-ty-10", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00500", @@ -5186,21 +5648,31 @@ "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-7", + "exception": "heldout_first_typo", + "item": "held-f-5", "record": "close", "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" + }, + { + "exception": "planted_swapped_dob", + "item": "plant-s-3", + "record": "close", + "record_why": [ + "same name, day and month swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" }, { "exception": "heldout_last_typo", - "item": "held-l-4", + "item": "held-l-11", "record": "close", "record_why": [ - "same date of birth and first name, another last name", "same date of birth, a similar name" ], "right": "no chart: duplicate", @@ -5226,6 +5698,86 @@ "right": "no chart: duplicate", "screen": "stop" }, + { + "exception": "pl_second_surname", + "item": "pl-ss-0", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "pl_second_surname", + "item": "pl-ss-1", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "pl_second_surname", + "item": "pl-ss-2", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "pl_second_surname", + "item": "pl-ss-3", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-6", + "record": "close", + "record_why": [ + "same first name, another last name, same phone, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "pl_second_surname", + "item": "pl-ss-4", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "pl_initial", + "item": "pl-in-0", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "pl_second_surname", + "item": "pl-ss-5", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "r2_prior_name_moved_dob_typo", "item": "r2-pn-0", @@ -5267,10 +5819,13 @@ "screen": "proceed" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-8", - "record": "none", - "record_why": [], + "exception": "heldout_last_typo", + "item": "held-l-5", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], "right": "no chart: duplicate", "screen": "stop" }, @@ -5284,14 +5839,6 @@ "right": "no chart: duplicate", "screen": "proceed" }, - { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-0", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "stop" - }, { "exception": "r2_prior_name_moved_dob_typo", "item": "r2-pn-5", @@ -5301,15 +5848,26 @@ ], "right": "no chart: duplicate", "screen": "proceed" + }, + { + "exception": "pl_initial", + "item": "pl-in-5", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" } ], "summary": { - "items": 622, + "items": 676, "new_chart_items": 440, "record": { - "caught_duplicates": 145, + "caught_duplicates": 199, "decisions": { - "close": 110, + "close": 164, "exact": 37, "none": 462, "unreadable": 13 @@ -5326,7 +5884,7 @@ "missed_duplicates": 24 }, "screen": { - "caught_duplicates": 139, + "caught_duplicates": 193, "false_stops": 252, "false_stops_by_exception": { "ambiguous_new_person": 7, @@ -5341,7 +5899,7 @@ "shared_dob": 33, "slow_save": 5 }, - "false_stops_per_100_items": 40.5, + "false_stops_per_100_items": 37.3, "missed_by_exception": { "heldout_married_dob_typo": 12, "limit_married_moved_dob_typo": 12, @@ -5349,6 +5907,6 @@ }, "missed_duplicates": 30 }, - "true_duplicates": 169 + "true_duplicates": 223 } } diff --git a/packages/flow/benchmark/dup_check/results/soak-seed7-55.json b/packages/flow/benchmark/dup_check/results/soak-seed7-55.json index 2b3fc8b38..9afe406de 100644 --- a/packages/flow/benchmark/dup_check/results/soak-seed7-55.json +++ b/packages/flow/benchmark/dup_check/results/soak-seed7-55.json @@ -24,6 +24,16 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "r2_name_swap", + "item": "r2-sw-11", + "record": "close", + "record_why": [ + "first and last name swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00004", @@ -33,21 +43,29 @@ "screen": "proceed" }, { - "exception": "heldout_last_typo", - "item": "held-l-1", + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-0", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "r2_year_transposed", + "item": "r2-ty-8", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-3", + "exception": "pl_initial", + "item": "pl-in-2", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -61,61 +79,48 @@ "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-6", + "exception": "r2_year_transposed", + "item": "r2-ty-2", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-3", - "record": "close", - "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" - ], - "right": "no chart: duplicate", + "exception": "malformed_dob", + "item": "ref-00006", + "record": "none", + "record_why": [], + "right": "new chart", "screen": "proceed" }, { - "exception": "planted_hyphenated", - "item": "plant-h-6", + "exception": "heldout_first_typo", + "item": "held-f-2", "record": "close", "record_why": [ - "same date of birth and first name, another last name", "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-9", + "exception": "r2_name_swap", + "item": "r2-sw-6", "record": "close", "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" + "first and last name swapped" ], "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "malformed_dob", - "item": "ref-00006", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" + "screen": "stop" }, { - "exception": "planted_hyphenated", - "item": "plant-h-3", + "exception": "pl_initial", + "item": "pl-in-1", "record": "close", "record_why": [ - "same date of birth and first name, another last name", "same date of birth, a similar name" ], "right": "no chart: duplicate", @@ -123,7 +128,7 @@ }, { "exception": "heldout_last_typo", - "item": "held-l-2", + "item": "held-l-0", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -133,7 +138,7 @@ }, { "exception": "heldout_last_typo", - "item": "held-l-0", + "item": "held-l-1", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -142,54 +147,50 @@ "screen": "stop" }, { - "exception": "planted_hyphenated", - "item": "plant-h-11", + "exception": "planted_married", + "item": "plant-m-9", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-5", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "planted_married", - "item": "plant-m-11", + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-3", "record": "close", "record_why": [ - "same date of birth and first name, another last name" + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-1", - "record": "none", - "record_why": [], + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-7", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { - "exception": "r2_name_swap", - "item": "r2-sw-10", + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-0", "record": "close", "record_why": [ - "first and last name swapped" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_hyphenated", - "item": "plant-h-5", + "exception": "pl_initial", + "item": "pl-in-7", "record": "close", "record_why": [ "same date of birth and first name, another last name", @@ -199,34 +200,44 @@ "screen": "stop" }, { - "exception": "planted_married", - "item": "plant-m-1", + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-9", "record": "close", "record_why": [ - "same date of birth and first name, another last name" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "r2_name_swap", - "item": "r2-sw-9", + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-10", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-4", "record": "close", "record_why": [ - "first and last name swapped" + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-8", + "exception": "heldout_last_typo", + "item": "held-l-7", "record": "close", "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "clean", @@ -236,9 +247,20 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "heldout_first_typo", + "item": "held-f-4", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "r2_year_transposed", - "item": "r2-ty-10", + "item": "r2-ty-6", "record": "close", "record_why": [ "same name, a mistyped date of birth" @@ -247,8 +269,8 @@ "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-4", + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-5", "record": "close", "record_why": [ "same date of birth and first name, another last name", @@ -258,25 +280,55 @@ "screen": "stop" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-7", + "exception": "heldout_first_typo", + "item": "held-f-3", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_married", - "item": "plant-m-2", + "exception": "heldout_married_dob_typo", + "item": "held-md-7", "record": "close", "record_why": [ - "same date of birth and first name, another last name" + "same first name, another last name, same phone, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "r2_name_swap", + "item": "r2-sw-1", + "record": "close", + "record_why": [ + "first and last name swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-5", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, + { + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-8", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, { "exception": "missing_required", "item": "ref-00008", @@ -286,80 +338,160 @@ "screen": "stop" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-3", + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-2", "record": "none", "record_why": [], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { - "exception": "planted_swapped_dob", - "item": "plant-s-4", + "exception": "heldout_last_typo", + "item": "held-l-9", "record": "close", "record_why": [ - "same name, day and month swapped" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-1", + "exception": "planted_swapped_dob", + "item": "plant-s-10", "record": "close", "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" + "same name, day and month swapped" ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { - "exception": "r2_name_swap", - "item": "r2-sw-2", + "exception": "planted_hyphenated", + "item": "plant-h-3", "record": "close", "record_why": [ - "first and last name swapped" + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-0", + "exception": "planted_hyphenated", + "item": "plant-h-11", "record": "close", "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" + "same date of birth, a similar name" ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { - "exception": "r2_name_swap", - "item": "r2-sw-5", + "exception": "planted_married", + "item": "plant-m-1", "record": "close", "record_why": [ - "first and last name swapped" + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "r2_name_swap", - "item": "r2-sw-8", + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-10", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "pl_ambiguous_same_person", + "item": "pl-as-1", "record": "close", "record_why": [ - "first and last name swapped" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_married", - "item": "plant-m-5", + "exception": "heldout_married_dob_typo", + "item": "held-md-0", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" + "same first name, another last name, same phone, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "planted_swapped_dob", + "item": "plant-s-4", + "record": "close", + "record_why": [ + "same name, day and month swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_married", + "item": "plant-m-4", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "pl_ambiguous_same_person", + "item": "pl-as-11", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "pl_ambiguous_same_person", + "item": "pl-as-5", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_married", + "item": "plant-m-11", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "pl_ambiguous_same_person", + "item": "pl-as-9", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "pl_initial", + "item": "pl-in-3", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -398,6 +530,17 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-8", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "concurrent_same", "item": "ref-00012", @@ -417,10 +560,11 @@ "screen": "proceed" }, { - "exception": "planted_hyphenated", - "item": "plant-h-9", + "exception": "heldout_first_typo", + "item": "held-f-6", "record": "close", "record_why": [ + "same date of birth and first name, another last name", "same date of birth, a similar name" ], "right": "no chart: duplicate", @@ -442,6 +586,26 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-11", + "record": "close", + "record_why": [ + "same first name, another last name, same phone, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-11", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00016", @@ -451,20 +615,23 @@ "screen": "proceed" }, { - "exception": "r2_name_swap", - "item": "r2-sw-3", + "exception": "pl_married_moved", + "item": "pl-mm-2", "record": "close", "record_why": [ - "first and last name swapped" + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-8", - "record": "none", - "record_why": [], + "exception": "planted_married", + "item": "plant-m-6", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" + ], "right": "no chart: duplicate", "screen": "stop" }, @@ -477,16 +644,16 @@ "screen": "proceed" }, { - "exception": "clean", - "item": "ref-00018", + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-11", "record": "none", "record_why": [], - "right": "new chart", + "right": "no chart: duplicate", "screen": "proceed" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-2", + "exception": "pl_ambiguous_same_person", + "item": "pl-as-10", "record": "close", "record_why": [ "same name, a mistyped date of birth" @@ -494,6 +661,40 @@ "right": "no chart: duplicate", "screen": "stop" }, + { + "exception": "planted_hyphenated", + "item": "plant-h-9", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "clean", + "item": "ref-00018", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-1", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-11", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "malformed_dob", "item": "ref-00019", @@ -521,28 +722,28 @@ "screen": "proceed" }, { - "exception": "r2_name_swap", - "item": "r2-sw-4", + "exception": "heldout_married_dob_typo", + "item": "held-md-3", "record": "close", "record_why": [ - "first and last name swapped" + "same first name, another last name, same phone, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-7", + "exception": "r2_name_swap", + "item": "r2-sw-9", "record": "close", "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" + "first and last name swapped" ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "planted_married", - "item": "plant-m-4", + "item": "plant-m-5", "record": "close", "record_why": [ "same date of birth and first name, another last name", @@ -551,6 +752,14 @@ "right": "no chart: duplicate", "screen": "stop" }, + { + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-2", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "dup_resend", "item": "ref-00021", @@ -570,26 +779,28 @@ "screen": "proceed" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-1", - "record": "none", - "record_why": [], + "exception": "heldout_married_dob_typo", + "item": "held-md-10", + "record": "close", + "record_why": [ + "same first name, another last name, same phone, a mistyped date of birth" + ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-5", + "exception": "heldout_married_dob_typo", + "item": "held-md-1", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same first name, another last name, same phone, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { - "exception": "heldout_first_typo", - "item": "held-f-1", + "exception": "planted_hyphenated", + "item": "plant-h-10", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -598,12 +809,10 @@ "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-6", - "record": "close", - "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" - ], + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-0", + "record": "none", + "record_why": [], "right": "no chart: duplicate", "screen": "proceed" }, @@ -615,6 +824,16 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-4", + "record": "close", + "record_why": [ + "same first name, another last name, same phone, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, { "exception": "ambiguous_same_person", "item": "ref-00024", @@ -641,6 +860,16 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "heldout_first_typo", + "item": "held-f-9", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "common_surname", "item": "ref-00027", @@ -657,22 +886,6 @@ "right": "new chart", "screen": "proceed" }, - { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-10", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-11", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "stop" - }, { "exception": "concurrent_same", "item": "ref-00029", @@ -683,14 +896,6 @@ "right": "no chart: duplicate", "screen": "stop" }, - { - "exception": "common_surname", - "item": "ref-00030", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, { "exception": "r2_name_swap", "item": "r2-sw-7", @@ -702,24 +907,35 @@ "screen": "stop" }, { - "exception": "clean", - "item": "ref-00031", + "exception": "heldout_first_typo", + "item": "held-f-8", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "common_surname", + "item": "ref-00030", "record": "none", "record_why": [], "right": "new chart", - "screen": "proceed" + "screen": "stop" }, { - "exception": "missing_optional", - "item": "ref-00032", + "exception": "clean", + "item": "ref-00031", "record": "none", "record_why": [], "right": "new chart", "screen": "proceed" }, { - "exception": "heldout_last_typo", - "item": "held-l-7", + "exception": "planted_hyphenated", + "item": "plant-h-5", "record": "close", "record_why": [ "same date of birth and first name, another last name", @@ -728,6 +944,14 @@ "right": "no chart: duplicate", "screen": "stop" }, + { + "exception": "missing_optional", + "item": "ref-00032", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, { "exception": "common_surname", "item": "ref-00033", @@ -737,22 +961,40 @@ "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-11", + "exception": "pl_married_moved", + "item": "pl-mm-10", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_hyphenated", - "item": "plant-h-4", + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-4", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_married", + "item": "plant-m-3", "record": "close", "record_why": [ "same date of birth and first name, another last name", - "same date of birth, a similar name" + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "pl_ambiguous_same_person", + "item": "pl-as-7", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -776,16 +1018,25 @@ "screen": "proceed" }, { - "exception": "heldout_last_typo", - "item": "held-l-3", + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-10", "record": "close", "record_why": [ - "same date of birth and first name, another last name", "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, + { + "exception": "r2_name_swap", + "item": "r2-sw-2", + "record": "close", + "record_why": [ + "first and last name swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00036", @@ -795,8 +1046,16 @@ "screen": "proceed" }, { - "exception": "heldout_last_typo", - "item": "held-l-8", + "exception": "clean", + "item": "ref-00037", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "pl_initial", + "item": "pl-in-5", "record": "close", "record_why": [ "same date of birth and first name, another last name", @@ -806,20 +1065,36 @@ "screen": "stop" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-9", - "record": "none", - "record_why": [], + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-6", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "clean", - "item": "ref-00037", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" + "exception": "r2_name_swap", + "item": "r2-sw-8", + "record": "close", + "record_why": [ + "first and last name swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "pl_initial", + "item": "pl-in-8", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" }, { "exception": "slow_save", @@ -838,32 +1113,26 @@ "screen": "proceed" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-7", + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-9", "record": "none", "record_why": [], "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "shared_dob", - "item": "ref-00040", - "record": "none", - "record_why": [], - "right": "new chart", "screen": "stop" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-5", - "record": "none", - "record_why": [], + "exception": "r2_year_transposed", + "item": "r2-ty-10", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth" + ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_married", - "item": "plant-m-10", + "exception": "pl_married_moved", + "item": "pl-mm-9", "record": "close", "record_why": [ "same date of birth and first name, another last name" @@ -872,21 +1141,29 @@ "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-6", + "exception": "shared_dob", + "item": "ref-00040", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "r2_year_transposed", + "item": "r2-ty-9", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_hyphenated", - "item": "plant-h-10", + "exception": "heldout_last_typo", + "item": "held-l-5", "record": "close", "record_why": [ + "same date of birth and first name, another last name", "same date of birth, a similar name" ], "right": "no chart: duplicate", @@ -902,10 +1179,9 @@ }, { "exception": "heldout_first_typo", - "item": "held-f-7", + "item": "held-f-10", "record": "close", "record_why": [ - "same date of birth and first name, another last name", "same date of birth, a similar name" ], "right": "no chart: duplicate", @@ -922,8 +1198,28 @@ "screen": "stop" }, { - "exception": "planted_married", - "item": "plant-m-9", + "exception": "pl_married_moved", + "item": "pl-mm-11", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "pl_ambiguous_same_person", + "item": "pl-as-6", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "pl_married_moved", + "item": "pl-mm-0", "record": "close", "record_why": [ "same date of birth and first name, another last name" @@ -933,41 +1229,60 @@ }, { "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-0", + "item": "lim-mm-6", "record": "none", "record_why": [], "right": "no chart: duplicate", "screen": "proceed" }, { - "exception": "heldout_first_typo", - "item": "held-f-2", + "exception": "pl_married_moved", + "item": "pl-mm-1", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-8", "record": "close", "record_why": [ + "same date of birth and first name, another last name", "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "clean", - "item": "ref-00042", + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-4", "record": "none", "record_why": [], - "right": "new chart", + "right": "no chart: duplicate", "screen": "proceed" }, { "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-6", + "item": "lim-mm-9", "record": "none", "record_why": [], "right": "no chart: duplicate", "screen": "proceed" }, + { + "exception": "clean", + "item": "ref-00042", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, { "exception": "planted_hyphenated", - "item": "plant-h-8", + "item": "plant-h-6", "record": "close", "record_why": [ "same date of birth and first name, another last name", @@ -985,51 +1300,47 @@ "screen": "proceed" }, { - "exception": "heldout_last_typo", - "item": "held-l-5", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-3", + "record": "none", + "record_why": [], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-4", + "exception": "r2_name_swap", + "item": "r2-sw-3", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "first and last name swapped" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-5", + "exception": "pl_married_moved", + "item": "pl-mm-4", "record": "close", "record_why": [ "same date of birth and first name, another last name", - "same date of birth, a similar name" + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-4", + "exception": "r2_year_transposed", + "item": "r2-ty-5", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-1", + "exception": "pl_ambiguous_same_person", + "item": "pl-as-3", "record": "close", "record_why": [ "same name, a mistyped date of birth" @@ -1038,24 +1349,45 @@ "screen": "stop" }, { - "exception": "dup_resend", - "item": "ref-00044", - "record": "exact", + "exception": "planted_swapped_dob", + "item": "plant-s-3", + "record": "close", "record_why": [ - "same first name, last name and date of birth" + "same name, day and month swapped" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-4", + "exception": "heldout_first_typo", + "item": "held-f-1", "record": "close", "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" + "same date of birth, a similar name" ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" + }, + { + "exception": "dup_resend", + "item": "ref-00044", + "record": "exact", + "record_why": [ + "same first name, last name and date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-3", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" }, { "exception": "dup_existing", @@ -1069,39 +1401,28 @@ "screen": "stop" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-11", + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-3", "record": "none", "record_why": [], "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-5", - "record": "close", - "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { - "exception": "r2_name_swap", - "item": "r2-sw-1", + "exception": "planted_hyphenated", + "item": "plant-h-1", "record": "close", "record_why": [ - "first and last name swapped" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_hyphenated", - "item": "plant-h-7", + "exception": "pl_initial", + "item": "pl-in-9", "record": "close", "record_why": [ - "same date of birth and first name, another last name", "same date of birth, a similar name" ], "right": "no chart: duplicate", @@ -1116,10 +1437,32 @@ "screen": "proceed" }, { - "exception": "heldout_last_typo", - "item": "held-l-9", + "exception": "pl_married_moved", + "item": "pl-mm-8", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-11", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-8", "record": "close", "record_why": [ + "same date of birth and first name, another last name", "same date of birth, a similar name" ], "right": "no chart: duplicate", @@ -1134,58 +1477,71 @@ "screen": "proceed" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-3", - "record": "none", - "record_why": [], + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-2", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { - "exception": "clean", - "item": "ref-00048", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" + "exception": "heldout_last_typo", + "item": "held-l-10", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-10", + "exception": "pl_ambiguous_same_person", + "item": "pl-as-4", "record": "close", "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-8", + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-8", "record": "none", "record_why": [], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-10", + "exception": "clean", + "item": "ref-00048", "record": "none", "record_why": [], - "right": "no chart: duplicate", - "screen": "stop" + "right": "new chart", + "screen": "proceed" }, { "exception": "planted_married", - "item": "plant-m-6", + "item": "plant-m-10", "record": "close", "record_why": [ - "same date of birth and first name, another last name", "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-6", + "record": "close", + "record_why": [ + "same first name, another last name, same phone, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, { "exception": "heldout_first_typo", "item": "held-f-0", @@ -1212,6 +1568,35 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "heldout_first_typo", + "item": "held-f-7", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-5", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "r2_year_transposed", + "item": "r2-ty-7", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00051", @@ -1232,105 +1617,264 @@ "screen": "stop" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-4", + "exception": "pl_married_moved", + "item": "pl-mm-3", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-2", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-4", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-2", + "record": "close", + "record_why": [ + "same first name, another last name, same phone, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-11", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "missing_optional", + "item": "ref-00053", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "common_surname", + "item": "ref-00054", "record": "none", "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-2", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-1", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_married", + "item": "plant-m-7", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "pl_married_moved", + "item": "pl-mm-5", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "r2_year_transposed", + "item": "r2-ty-1", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "missing_required", + "item": "ref-00055", + "record": "unreadable", + "record_why": [], + "right": "no chart", + "screen": "stop" + }, + { + "exception": "r2_nickname", + "item": "r2-nk-0", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-6", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "pl_ambiguous_same_person", + "item": "pl-as-0", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "r2_year_transposed", + "item": "r2-ty-4", + "record": "close", + "record_why": [ + "same name, a mistyped date of birth" + ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_swapped_dob", - "item": "plant-s-3", + "exception": "r2_nickname", + "item": "r2-nk-1", "record": "close", "record_why": [ - "same name, day and month swapped" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-3", + "exception": "pl_initial", + "item": "pl-in-10", "record": "close", "record_why": [ - "same date of birth and first name, another last name", "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "missing_optional", - "item": "ref-00053", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "heldout_first_typo", - "item": "held-f-10", + "exception": "planted_married", + "item": "plant-m-2", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-8", + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-1", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "r2_nickname", + "item": "r2-nk-2", "record": "close", "record_why": [ - "same date of birth and first name, another last name", "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "common_surname", - "item": "ref-00054", + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-7", "record": "none", "record_why": [], - "right": "new chart", + "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_married", - "item": "plant-m-3", + "exception": "pl_ambiguous_same_person", + "item": "pl-as-8", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-2", + "exception": "r2_nickname", + "item": "r2-nk-3", "record": "close", "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" + "same date of birth, a similar name" ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-9", + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-4", "record": "close", "record_why": [ + "same date of birth and first name, another last name", "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_hyphenated", - "item": "plant-h-2", + "exception": "r2_nickname", + "item": "r2-nk-4", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -1339,26 +1883,18 @@ "screen": "stop" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-6", + "exception": "r2_name_swap", + "item": "r2-sw-10", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "first and last name swapped" ], "right": "no chart: duplicate", "screen": "stop" }, - { - "exception": "missing_required", - "item": "ref-00055", - "record": "unreadable", - "record_why": [], - "right": "no chart", - "screen": "stop" - }, { "exception": "r2_nickname", - "item": "r2-nk-0", + "item": "r2-nk-5", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -1367,65 +1903,69 @@ "screen": "stop" }, { - "exception": "planted_hyphenated", - "item": "plant-h-1", + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-5", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "r2_name_swap", + "item": "r2-sw-5", "record": "close", "record_why": [ - "same date of birth, a similar name" + "first and last name swapped" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "r2_nickname", - "item": "r2-nk-1", + "exception": "r2_year_transposed", + "item": "r2-ty-3", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-11", + "exception": "pl_married_moved", + "item": "pl-mm-6", "record": "close", "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-2", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_married", - "item": "plant-m-7", + "exception": "pl_initial", + "item": "pl-in-4", "record": "close", "record_why": [ "same date of birth and first name, another last name", - "same date of birth and first name, another last name" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-2", - "record": "none", - "record_why": [], + "exception": "planted_hyphenated", + "item": "plant-h-7", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { - "exception": "r2_nickname", - "item": "r2-nk-2", + "exception": "pl_second_surname", + "item": "pl-ss-0", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -1434,42 +1974,42 @@ "screen": "stop" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-8", + "exception": "pl_second_surname", + "item": "pl-ss-1", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_married", - "item": "plant-m-8", + "exception": "pl_initial", + "item": "pl-in-6", "record": "close", "record_why": [ "same date of birth and first name, another last name", - "same date of birth and first name, another last name" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "r2_nickname", - "item": "r2-nk-3", + "exception": "r2_name_swap", + "item": "r2-sw-0", "record": "close", "record_why": [ - "same date of birth, a similar name" + "first and last name swapped" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-9", + "exception": "pl_second_surname", + "item": "pl-ss-2", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -1483,8 +2023,8 @@ "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-10", + "exception": "pl_second_surname", + "item": "pl-ss-3", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -1493,8 +2033,8 @@ "screen": "stop" }, { - "exception": "r2_nickname", - "item": "r2-nk-4", + "exception": "pl_second_surname", + "item": "pl-ss-4", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -1503,8 +2043,26 @@ "screen": "stop" }, { - "exception": "r2_nickname", - "item": "r2-nk-5", + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-7", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-5", + "record": "close", + "record_why": [ + "same first name, another last name, same phone, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "pl_initial", + "item": "pl-in-11", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -1513,40 +2071,44 @@ "screen": "stop" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-9", - "record": "none", - "record_why": [], + "exception": "heldout_married_dob_typo", + "item": "held-md-8", + "record": "close", + "record_why": [ + "same first name, another last name, same phone, a mistyped date of birth" + ], "right": "no chart: duplicate", "screen": "proceed" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-4", - "record": "none", - "record_why": [], + "exception": "pl_initial", + "item": "pl-in-0", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { - "exception": "r2_prior_name_moved_dob_typo", - "item": "r2-pn-0", + "exception": "pl_second_surname", + "item": "pl-ss-5", "record": "close", "record_why": [ - "same name, a mistyped date of birth (a prior name on the chart)" + "same date of birth, a similar name" ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { - "exception": "planted_swapped_dob", - "item": "plant-s-10", + "exception": "r2_prior_name_moved_dob_typo", + "item": "r2-pn-0", "record": "close", "record_why": [ - "same name, day and month swapped" + "same name, a mistyped date of birth (a prior name on the chart)" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "r2_prior_name_moved_dob_typo", @@ -1560,7 +2122,7 @@ }, { "exception": "r2_name_swap", - "item": "r2-sw-0", + "item": "r2-sw-4", "record": "close", "record_why": [ "first and last name swapped" @@ -1569,22 +2131,14 @@ "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-11", + "exception": "heldout_married_dob_typo", + "item": "held-md-9", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same first name, another last name, same phone, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-7", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "r2_prior_name_moved_dob_typo", @@ -1597,18 +2151,19 @@ "screen": "proceed" }, { - "exception": "r2_prior_name_moved_dob_typo", - "item": "r2-pn-3", + "exception": "planted_married", + "item": "plant-m-8", "record": "close", "record_why": [ - "same name, a mistyped date of birth (a prior name on the chart)" + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "r2_prior_name_moved_dob_typo", - "item": "r2-pn-4", + "item": "r2-pn-3", "record": "close", "record_why": [ "same name, a mistyped date of birth (a prior name on the chart)" @@ -1617,22 +2172,25 @@ "screen": "proceed" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-0", - "record": "none", - "record_why": [], + "exception": "pl_married_moved", + "item": "pl-mm-7", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" + ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "r2_name_swap", - "item": "r2-sw-6", + "exception": "r2_prior_name_moved_dob_typo", + "item": "r2-pn-4", "record": "close", "record_why": [ - "first and last name swapped" + "same name, a mistyped date of birth (a prior name on the chart)" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "r2_prior_name_moved_dob_typo", @@ -1645,23 +2203,23 @@ "screen": "proceed" }, { - "exception": "r2_name_swap", - "item": "r2-sw-11", + "exception": "pl_ambiguous_same_person", + "item": "pl-as-2", "record": "close", "record_why": [ - "first and last name swapped" + "same name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" } ], "summary": { - "items": 177, + "items": 231, "new_chart_items": 45, "record": { - "caught_duplicates": 106, + "caught_duplicates": 160, "decisions": { - "close": 99, + "close": 153, "exact": 7, "none": 69, "unreadable": 2 @@ -1676,7 +2234,7 @@ "missed_duplicates": 24 }, "screen": { - "caught_duplicates": 100, + "caught_duplicates": 154, "false_stops": 10, "false_stops_by_exception": { "clean": 2, @@ -1684,7 +2242,7 @@ "shared_dob": 3, "slow_save": 1 }, - "false_stops_per_100_items": 5.6, + "false_stops_per_100_items": 4.3, "missed_by_exception": { "heldout_married_dob_typo": 12, "limit_married_moved_dob_typo": 12, @@ -1692,6 +2250,6 @@ }, "missed_duplicates": 30 }, - "true_duplicates": 130 + "true_duplicates": 184 } } diff --git a/packages/flow/docs/DUPLICATE_CHECK.md b/packages/flow/docs/DUPLICATE_CHECK.md index 066b573dc..b1c4afc98 100644 --- a/packages/flow/docs/DUPLICATE_CHECK.md +++ b/packages/flow/docs/DUPLICATE_CHECK.md @@ -80,7 +80,7 @@ It reads every page, and it decides from whole records: | Decision | When | What happens | |---|---|---| | `exact` | same first name, last name and date of birth, or the same MRN | stop: the chart exists | -| `close` | same name (or a nickname, or the chart's prior name) with day and month swapped, or with the date of birth one field off and either the exact name or a shared phone, email or street; same date of birth with a similar name or a shared hyphenated part; first and last name swapped; same date of birth and first name under another last name; another name with the same date of birth and a shared contact detail, or a near date of birth and two | stop for a person; the halt names each record and why | +| `close` | same name (or a nickname, an initial, or the chart's prior name) with day and month swapped, or with the date of birth one field off and either the exact name or a shared phone, email or street; same date of birth with a similar name or a shared hyphenated part; first and last name swapped; same date of birth and first name under another last name; another name with the same date of birth and a shared contact detail, or a near date of birth and two | stop for a person; the halt names each record and why | | `none` | no record is this patient | proceed | | `unreadable` | unreachable, refused credentials, not a result set, a reported total that doesn't match what was read, more than 50 pages, a full REST page with no next link, a missing or non-ISO date of birth | stop (fail closed) | diff --git a/packages/flow/openadapt_flow/runtime/checks/duplicate_record.py b/packages/flow/openadapt_flow/runtime/checks/duplicate_record.py index b3cbecc18..a348e5660 100644 --- a/packages/flow/openadapt_flow/runtime/checks/duplicate_record.py +++ b/packages/flow/openadapt_flow/runtime/checks/duplicate_record.py @@ -259,11 +259,14 @@ def one_field_off(a: str, b: str) -> bool: def same_given(a: str, b: str) -> bool: - """The same first name: similar spelling, or a known nickname of it.""" + """The same first name: similar spelling, an initial, or a known nickname.""" if similar_name(a, b): return True x, y = norm_name(a), norm_name(b) + # An initial ("J." for James) stands for any name with that first letter. + if x and y and (len(x) == 1 or len(y) == 1) and x[0] == y[0]: + return True return bool(x and y and y in NICKNAMES.get(x, ())) diff --git a/packages/flow/tests/test_duplicate_record_check.py b/packages/flow/tests/test_duplicate_record_check.py index bfca8b1e8..2997661fb 100644 --- a/packages/flow/tests/test_duplicate_record_check.py +++ b/packages/flow/tests/test_duplicate_record_check.py @@ -389,3 +389,30 @@ def test_fhir_reads_old_names_and_contacts(serve) -> None: assert result.decision == "close" record = result.candidates[0].record assert record.prior_names == (("Rosalind", "Jesswood"),) and record.phone + + +@pytest.mark.parametrize( + ("want", "record"), + [ + # An initial for the first name. + ( + {"fname": "J.", "lname": "Ashby", "DOB": "1950-02-03"}, + rec("James", "Ashby", "1950-02-03"), + ), + # A second surname on the chart, the first one on the referral. + ( + {"fname": "Lucia", "lname": "Garcia", "DOB": "1981-06-15"}, + rec("Lucia", "Garcia Lopez", "1981-06-15"), + ), + ], +) +def test_production_lifecycle_cases_stop( + want: dict[str, str], record: PatientRecord +) -> None: + found = classify(want, record) + assert found is not None and found.match == "close" + + +def test_an_initial_does_not_match_another_letter() -> None: + want = {"fname": "J.", "lname": "Ashby", "DOB": "1950-02-03"} + assert classify(want, rec("Mary", "Ashby", "1950-02-03")) is None From 7be163d9ed98ec0db89a12c8b7ecf0252f496161 Mon Sep 17 00:00:00 2001 From: abrichr Date: Sat, 24 Oct 2026 20:04:28 -0400 Subject: [PATCH 05/13] docs: duplicate check, the exact test-set size and misses by kind Co-Authored-By: Claude Opus 5.5 --- packages/flow/benchmark/dup_check/README.md | 50 +++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/packages/flow/benchmark/dup_check/README.md b/packages/flow/benchmark/dup_check/README.md index 0b8dfffd4..750077343 100644 --- a/packages/flow/benchmark/dup_check/README.md +++ b/packages/flow/benchmark/dup_check/README.md @@ -30,6 +30,56 @@ married name plus a mistyped day of birth. Each item meets the existing charts, every earlier item's right outcome from the soak's truth, and another clerk's chart for `concurrent_same`. +## The exact numbers + +A miss is a second chart for a patient who already has one. + +**Test set (extended stream):** 676 items, 440 new patients and 223 true +duplicates. +- 47 duplicates come from the soak's own seed-7 generator: 12 existing + patients referred again, 19 resends, 10 same people with a mistyped date of + birth, and 6 registered by another clerk moments before. +- 176 are planted variants of the existing patients. + +The first report's "12 of 110" was the same stream before the second and +third rounds of planted kinds were added. Each round grew the set; the misses +below are on the full 223. + +| | screen check | record check | +|---|---:|---:| +| missed, of 223 | 30 | 24 | +| missed, of the 47 from the generator | 0 | 0 | +| missed, of the 176 planted | 30 | 24 | +| false stops, of 440 new patients | 252 | 2 | + +The record check's 24 misses, all a known limit (uncatchable without extra +data): +- 12 `limit_married_moved_dob_typo`: a new last name, a new address, phone + and email, and a mistyped day of birth; +- 12 `limit_new_name_moved_same_dob`: new first and last names and new + contact details, with the same date of birth. + +The referral shares nothing with the chart except, in the second kind, a date +of birth that thousands of patients share. Catching them needs an MRN, an +insurance member id, or a person. The screen stops the second kind only +because it stops every new patient who shares a date of birth with anyone. + +The screen check's 30 misses: +- 12 `heldout_married_dob_typo`; +- 12 `limit_married_moved_dob_typo`; +- 6 `r2_prior_name_moved_dob_typo`. + +The record check catches the first and third of these. + +The soak's own 55 items with the same planting: 231 items, 184 true +duplicates (8 from the generator, 176 planted). Screen: 30 missed, 10 false +stops. Record: 24 missed (the same two limit kinds), 0 false stops. + +The planted kinds not in the table below (all caught by both checks): +- `planted_married`: a married name, everything else the same; +- `heldout_first_typo` and `heldout_last_typo`: one transposed letter; +- 12 of each. + ## Results (2026-10-24, second round) The first version of the record check missed 12 of 110 planted duplicates, From 09d0298dcbdda56ccc3a205b3cdea01f87d361e1 Mon Sep 17 00:00:00 2001 From: abrichr Date: Sat, 24 Oct 2026 20:25:25 -0400 Subject: [PATCH 06/13] duplicate check: one source for every Replayer; a program can pin the record check The record source was set only in deployment.build_replayer, so qualification (qualify/campaign.py), the lifecycle proof's run, execute and resume built Replayers that silently used the on-screen list. Replayer now resolves the source itself from the effect verifier it is given (resolve_duplicate_source), so every path that reads the same system of record uses the same check. The postcondition's candidate_source: record pins the record check into the qualified program; without a channel the step fails closed instead of reading the screen. The soak's OpenEMR program change pins it. tests/test_duplicate_source_paths.py scans every Replayer(...) construction: none picks a source of its own, the governed paths pass their verifier, the same verifier gives the same source through build_replayer and directly, and a pinned program fails closed without a channel. RestPatientSource and SqlPatientSource are labeled OpenEMR adapters; FhirPatientSource generic R4. Co-Authored-By: Claude Opus 5.5 --- .../benchmark/lifecycle/production_arm.py | 3 + packages/flow/docs/DUPLICATE_CHECK.md | 31 ++- packages/flow/openadapt_flow/deployment.py | 18 +- packages/flow/openadapt_flow/ir.py | 13 +- .../runtime/checks/duplicate_record.py | 40 +++- .../flow/openadapt_flow/runtime/replayer.py | 33 +++- .../flow/tests/test_duplicate_source_paths.py | 183 ++++++++++++++++++ 7 files changed, 292 insertions(+), 29 deletions(-) create mode 100644 packages/flow/tests/test_duplicate_source_paths.py diff --git a/packages/flow/benchmark/lifecycle/production_arm.py b/packages/flow/benchmark/lifecycle/production_arm.py index 3e1dfcbe0..f13c03145 100644 --- a/packages/flow/benchmark/lifecycle/production_arm.py +++ b/packages/flow/benchmark/lifecycle/production_arm.py @@ -772,6 +772,9 @@ def with_duplicate_list_check(source: Path, out: Path) -> Path: candidate_rows="tr.oneresult", candidate_frame_path=("#modalframe",), candidate_params=("lname", "DOB"), + # Qualified with the record check (OpenEMR's patient API): a run + # without that channel fails the step instead of reading the list. + candidate_source="record", ) for pc in step.expect ] diff --git a/packages/flow/docs/DUPLICATE_CHECK.md b/packages/flow/docs/DUPLICATE_CHECK.md index b1c4afc98..37e52f1ac 100644 --- a/packages/flow/docs/DUPLICATE_CHECK.md +++ b/packages/flow/docs/DUPLICATE_CHECK.md @@ -64,13 +64,34 @@ on the OpenEMR referral program, and the soak admits that version afresh ## Asking the system of record instead -When the deployment's `effects` reads a FHIR server or OpenEMR's patient API, +When the run's effect verifier reads a FHIR server or OpenEMR's patient API, `no_duplicate_candidate` searches the system of record for the patient instead of reading the on-screen list (`runtime/checks/duplicate_record.py`, a check -whose source is the record). `runtime.duplicate_check: screen` keeps the -on-screen list. With no such channel, the on-screen list decides, as above. A -read-only SQL query on `patient_data` (`SqlPatientSource`) is the documented -fallback for a deployment with neither; it is wired by hand, not by default. +whose source is the record). With no such channel, the on-screen list decides, +as above. + +The channels: + +| Source | Scope | +|---|---| +| `FhirPatientSource` | generic FHIR R4 (`Patient` search with standard parameters); any R4 server | +| `RestPatientSource` | OpenEMR adapter: OpenEMR's standard REST `/api/patient` | +| `SqlPatientSource` | OpenEMR adapter, fallback: a read-only query on `patient_data`, wired by hand, never by default | + +**One source for every path.** `Replayer` resolves the source itself, from the +effect verifier it is given (`resolve_duplicate_source`). So qualification +(`qualify`), a run (`build_replayer`), a resume and the lifecycle proof that +read the same system of record use the same check, and none of them can pick +another. `tests/test_duplicate_source_paths.py` scans every `Replayer(...)` +construction to keep it so. + +**The program can pin it.** The postcondition's `candidate_source: record` +makes the record check part of the qualified program: a run with no record +channel fails the step, and never reads the screen instead. +`candidate_source: screen` keeps the list even when a channel exists. Unset, +the run's channel decides. The deployment's `runtime.duplicate_check: screen` +turns the record check off for unpinned programs; a program pinned to +`record` then fails closed. The check searches by the date of birth, by the date of birth with day and month swapped, by each part of the last and first name, by first name, and by diff --git a/packages/flow/openadapt_flow/deployment.py b/packages/flow/openadapt_flow/deployment.py index 7a102f2f9..3aaf25f63 100644 --- a/packages/flow/openadapt_flow/deployment.py +++ b/packages/flow/openadapt_flow/deployment.py @@ -761,13 +761,13 @@ class RuntimeSection(BaseModel): #: :func:`openadapt_flow.runtime.identity.verify_pixel_identity` and #: docs/LIMITS.md. pixel_verify_enabled: bool = False - #: Where ``no_duplicate_candidate`` gets its evidence. ``record`` (the + #: Where ``no_duplicate_candidate`` gets its evidence. ``auto`` (the #: default): when ``effects`` reads a FHIR server or OpenEMR's patient API, #: the check searches the system of record for this patient #: (``runtime/checks/duplicate_record.py``) and the app's on-screen match #: list is not read. With no such channel, or ``screen``, the on-screen #: list decides, as before. - duplicate_check: Literal["record", "screen"] = "record" + duplicate_check: Literal["auto", "screen"] = "auto" #: Path to an export of the system of record that lists every identifier #: in the namespace (CSV, JSON or one per line). When set, an OCR identity #: check that abstained only on a look-alike identifier (O/0, l/1, B/8) is @@ -1447,16 +1447,6 @@ def build_model_grounder(cfg: GroundingModelConfig) -> Optional[Any]: return None -def _duplicate_record_source(effect_verifier: Any) -> Any: - """The duplicate check's record channel, from the deployment's verifier.""" - - if effect_verifier is None: - return None - from openadapt_flow.runtime.checks.duplicate_record import source_for_verifier - - return source_for_verifier(effect_verifier) - - def build_replayer( backend: Any, *, @@ -1668,9 +1658,7 @@ def build_replayer( allow_model_grounding=allow_egress, effect_verifier=effect_verifier, duplicate_record_source=( - None - if runtime_config is not None and runtime_config.duplicate_check == "screen" - else _duplicate_record_source(effect_verifier) + runtime_config.duplicate_check if runtime_config is not None else "auto" ), api_actuator=api_actuator, durable=durable, diff --git a/packages/flow/openadapt_flow/ir.py b/packages/flow/openadapt_flow/ir.py index 9a3fd2cbd..539e23ee4 100644 --- a/packages/flow/openadapt_flow/ir.py +++ b/packages/flow/openadapt_flow/ir.py @@ -514,6 +514,12 @@ class Postcondition(BaseModel): candidate_params: Optional[tuple[str, ...]] = Field( default=None, min_length=1, max_length=8 ) + #: NO_DUPLICATE_CANDIDATE: where the check's evidence must come from. + #: ``record``: the system of record, and a run with no record channel + #: fails the step (never the screen instead). ``screen``: the listed rows, + #: even when a record channel exists. Unset: the record when the run has + #: a record channel, else the screen. + candidate_source: Optional[Literal["record", "screen"]] = None @model_serializer(mode="wrap") def _serialize_compatible(self, handler: Any) -> dict[str, Any]: @@ -525,7 +531,12 @@ def _serialize_compatible(self, handler: Any) -> dict[str, Any]: data.pop("field_selector", None) if self.param is None: data.pop("param", None) - for name in ("candidate_rows", "candidate_frame_path", "candidate_params"): + for name in ( + "candidate_rows", + "candidate_frame_path", + "candidate_params", + "candidate_source", + ): if getattr(self, name) is None: data.pop(name, None) return data diff --git a/packages/flow/openadapt_flow/runtime/checks/duplicate_record.py b/packages/flow/openadapt_flow/runtime/checks/duplicate_record.py index a348e5660..c093e7721 100644 --- a/packages/flow/openadapt_flow/runtime/checks/duplicate_record.py +++ b/packages/flow/openadapt_flow/runtime/checks/duplicate_record.py @@ -484,7 +484,11 @@ def _get_json( class FhirPatientSource: - """FHIR R4 ``Patient`` search, every page (``Bundle.link[next]``). + """Generic FHIR R4: ``Patient`` search, every page (``Bundle.link[next]``). + + Uses only standard R4 search parameters (``birthdate``, ``family``, + ``given``, ``identifier``, ``phone``, ``email``), so it serves any R4 + server, OpenEMR's included. A page chain longer than :data:`MAX_PAGES`, a ``Bundle.total`` that does not equal the entries read, or an entry that is not a ``Patient`` makes @@ -648,7 +652,10 @@ def by_email(self, email: str) -> list[PatientRecord]: class SqlPatientSource: - """Fallback: a read-only query on OpenEMR's ``patient_data``. + """OpenEMR adapter, fallback: a read-only query on ``patient_data``. + + The columns (``pid``, ``pubpid``, ``DOB``, ``birth_lname``, ...) are + OpenEMR's. For a deployment with no FHIR or REST read. ``connect`` returns a DB-API connection with a SELECT-only account. Uses ``qmark`` parameters. @@ -774,7 +781,10 @@ def by_email(self, email: str) -> list[PatientRecord]: class RestPatientSource: - """OpenEMR's standard REST API: ``GET .../api/patient?DOB=...``. + """OpenEMR adapter: its standard REST API, ``GET .../api/patient?DOB=...``. + + The query parameters and fields (``fname``, ``lname``, ``DOB``, + ``pubpid``, ``phone_home``) are OpenEMR's. OpenEMR answers with ``{"data": [...]}``. A next-page link (``links.next``) is followed. A full page with no next link may be @@ -930,3 +940,27 @@ def token() -> Optional[str]: timeout_s=max(verifier.timeout_s, 10.0), ) return None + + +#: ``Replayer(duplicate_record_source=...)``: derive the source from the run's +#: own effect verifier (the default), or keep the on-screen list. +AUTO = "auto" +SCREEN = "screen" + + +def resolve_duplicate_source(effect_verifier: Any, requested: Any = AUTO) -> Any: + """The one place a run's duplicate-check source is decided. + + Every ``Replayer`` resolves its source here from the effect verifier it is + given, so qualification, a run, a resume and a proof that read the same + system of record use the same check. ``requested`` is ``AUTO`` (derive), + ``SCREEN`` (no record source) or a source object (tests, embedding). + """ + + if requested == SCREEN or requested is None: + return None + if requested != AUTO: + return requested + if effect_verifier is None: + return None + return source_for_verifier(effect_verifier) diff --git a/packages/flow/openadapt_flow/runtime/replayer.py b/packages/flow/openadapt_flow/runtime/replayer.py index 5a8438b3b..ce8c6d321 100644 --- a/packages/flow/openadapt_flow/runtime/replayer.py +++ b/packages/flow/openadapt_flow/runtime/replayer.py @@ -868,7 +868,7 @@ def __init__( state_verifier: Optional[Any] = None, effect_verifier: Optional[EffectVerifier] = None, effect_compensator: Optional[Any] = None, - duplicate_record_source: Optional[Any] = None, + duplicate_record_source: Any = "auto", api_actuator: Optional[Any] = None, web_form_actuator: Optional[Any] = None, api_steps: bool = True, @@ -1022,9 +1022,18 @@ def __init__( self.effect_verifier = effect_verifier self.effect_compensator = effect_compensator # The duplicate check's record channel (``runtime/checks/ - # duplicate_record.py``). When set, ``no_duplicate_candidate`` asks the - # system of record instead of reading the app's match list. - self.duplicate_record_source = duplicate_record_source + # duplicate_record.py``), resolved here from this run's own effect + # verifier, so every construction path (qualification, run, resume, + # proof) that reads the same system of record uses the same check. + # When set, ``no_duplicate_candidate`` asks the system of record + # instead of reading the app's match list. + from openadapt_flow.runtime.checks.duplicate_record import ( + resolve_duplicate_source, + ) + + self.duplicate_record_source = resolve_duplicate_source( + effect_verifier, duplicate_record_source + ) self._last_duplicate_check: Optional[Any] = None # ``run``-only, bundle-bound admission capability. The ordinary # ``replay`` path leaves this unset and preserves its permissive @@ -17739,7 +17748,21 @@ def _no_duplicate_candidate(self, pc: Any, frame_png: bytes) -> bool: or {} ) source = getattr(self, "duplicate_record_source", None) - if source is not None: + pinned = getattr(pc, "candidate_source", None) + if pinned == "record" and source is None: + # The program was qualified with the record check; without a + # record channel it must not fall back to the screen. + from openadapt_flow.runtime.checks.duplicate_record import ( + DuplicateCheckResult, + ) + + self._last_duplicate_check = DuplicateCheckResult( + "unreadable", + reason="the program requires the record check and this run has " + "no record channel", + ) + return False + if source is not None and pinned != "screen": return self._no_duplicate_record(source, params) names = tuple(pc.candidate_params or ()) if not pc.candidate_rows or not names or not pc.text: diff --git a/packages/flow/tests/test_duplicate_source_paths.py b/packages/flow/tests/test_duplicate_source_paths.py new file mode 100644 index 000000000..bac6ec223 --- /dev/null +++ b/packages/flow/tests/test_duplicate_source_paths.py @@ -0,0 +1,183 @@ +"""Every governed way of building a Replayer resolves one duplicate-check source. + +Qualification, a run, a resume and the lifecycle proof must use the same +duplicate check, or a program is admitted under one check and run under +another. The source is resolved inside ``Replayer`` from the run's own effect +verifier (``resolve_duplicate_source``). These tests pin that: + +- no construction path picks a source of its own (an AST scan of every + ``Replayer(...)`` call); +- the governed paths all hand the Replayer their effect verifier; +- the same verifier gives the same source on every path; +- a program pinned to the record check fails closed without a channel. +""" + +from __future__ import annotations + +import ast +from pathlib import Path +from typing import Any + +import pytest + +from openadapt_flow.ir import Postcondition, PostconditionKind +from openadapt_flow.runtime.checks.duplicate_record import ( + FhirPatientSource, + PatientRecord, + RestPatientSource, + TableSource, + resolve_duplicate_source, +) +from openadapt_flow.runtime.effects import FhirEffectVerifier +from openadapt_flow.runtime.effects.rest import RestRecordVerifier + +FLOW = Path(__file__).resolve().parents[1] +#: Governed construction paths: (file, enclosing function) of a Replayer(...) +#: call that runs a program against the system of record. +GOVERNED = { + ("openadapt_flow/qualify/campaign.py", "_run"), + ("benchmark/lifecycle_proof/run.py", "run"), + ("benchmark/lifecycle_proof/run.py", "execute"), + ("benchmark/lifecycle_proof/run.py", "_resume"), + ("openadapt_flow/deployment.py", "build_replayer"), +} + + +def _replayer_calls() -> list[tuple[str, str, ast.Call]]: + out = [] + for root in ("openadapt_flow", "benchmark"): + for path in (FLOW / root).rglob("*.py"): + try: + tree = ast.parse(path.read_text(encoding="utf-8")) + except (SyntaxError, UnicodeDecodeError): + continue + rel = str(path.relative_to(FLOW)) + for fn in ast.walk(tree): + if not isinstance(fn, (ast.FunctionDef, ast.AsyncFunctionDef)): + continue + for node in ast.walk(fn): + if ( + isinstance(node, ast.Call) + and isinstance(node.func, ast.Name) + and node.func.id == "Replayer" + ): + out.append((rel, fn.name, node)) + return out + + +def test_no_construction_path_picks_its_own_duplicate_source() -> None: + calls = _replayer_calls() + assert calls, "the scan found no Replayer constructions" + choosing = { + (rel, fn) + for rel, fn, call in calls + if any(k.arg == "duplicate_record_source" for k in call.keywords) + } + # Only the deployment passes the operator's switch (auto or screen), + # never a source object. + assert choosing == {("openadapt_flow/deployment.py", "build_replayer")} + + +def test_every_governed_path_hands_the_replayer_its_effect_verifier() -> None: + calls = _replayer_calls() + found = {(rel, fn) for rel, fn, _ in calls} + missing = GOVERNED - found + assert not missing, f"governed paths moved or renamed: {sorted(missing)}" + for rel, fn, call in calls: + if (rel, fn) not in GOVERNED: + continue + names = {k.arg for k in call.keywords} + # ``**kwargs`` (qualify) must carry it; checked below by name. + assert "effect_verifier" in names or None in names, (rel, fn) + campaign = (FLOW / "openadapt_flow/qualify/campaign.py").read_text() + assert "effect_verifier=verifier" in campaign + + +class _Backend: + url, page_title, page_count = "http://x/", "x", 1 + + +def _fhir() -> FhirEffectVerifier: + return FhirEffectVerifier( + "https://emr.example.test/apis/default/fhir", access_token="t" + ) + + +def _rest() -> RestRecordVerifier: + return RestRecordVerifier( + "https://emr.example.test", + records_path="/apis/default/api/patient?_limit=1000", + records_key="data", + headers={"Authorization": "Bearer t"}, + ) + + +@pytest.mark.parametrize( + ("verifier", "kind"), [(_fhir, FhirPatientSource), (_rest, RestPatientSource)] +) +def test_the_same_verifier_gives_the_same_source_on_every_path(verifier, kind) -> None: + from openadapt_flow.deployment import build_replayer + from openadapt_flow.runtime.replayer import Replayer + from tests.test_replayer import FakeVision + + v = verifier() + direct = Replayer(_Backend(), vision=FakeVision(), effect_verifier=v) + deployed = build_replayer( + _Backend(), + allow_egress=False, + effect_verifier=v, + api_actuator=None, + durable=False, + use_structural=False, + ) + for replayer in (direct, deployed): + assert isinstance(replayer.duplicate_record_source, kind) + assert replayer.duplicate_record_source.base_url == v.base_url + + +def test_no_record_channel_means_the_screen_unless_the_program_pins_record() -> None: + assert resolve_duplicate_source(None) is None + assert resolve_duplicate_source(_fhir(), "screen") is None + table = TableSource([]) + assert resolve_duplicate_source(None, table) is table + + +def _check(source: Any, pinned: Any) -> tuple[bool, list[Any]]: + from openadapt_flow.runtime.replayer import Replayer + from tests.test_no_duplicate_candidate import MARKER + from tests.test_no_duplicate_candidate import _Backend as ListBackend + from tests.test_replayer import FakeVision + + backend = ListBackend([], marker=True) # the screen would pass + replayer = Replayer(backend, vision=FakeVision(), duplicate_record_source=source) + replayer._current_params = {"fname": "Ada", "lname": "Quill", "DOB": "1984-03-07"} + pc = Postcondition( + kind=PostconditionKind.NO_DUPLICATE_CANDIDATE, + text=MARKER, + candidate_rows="tr.oneresult", + candidate_frame_path=("#modalframe",), + candidate_params=("lname", "DOB"), + candidate_source=pinned, + ) + return replayer._postcondition_passes(pc, b"", None, {}), backend.read + + +def test_a_program_pinned_to_the_record_fails_closed_without_a_channel() -> None: + passed, read = _check("screen", "record") + assert passed is False and read == [] # never the screen instead + + +def test_a_program_pinned_to_the_screen_reads_the_screen() -> None: + table = TableSource([PatientRecord("1", "Ada", "Quill", "1984-03-07")]) + passed, read = _check(table, "screen") + assert passed is True and read # the list was read; the record wasn't asked + + +def test_unset_candidate_source_serializes_as_before() -> None: + pc = Postcondition( + kind=PostconditionKind.NO_DUPLICATE_CANDIDATE, + text="x", + candidate_rows="tr", + candidate_params=("lname",), + ) + assert "candidate_source" not in pc.model_dump() From 2d2894d84a217963e00786458f3ee9a151e7f6e0 Mon Sep 17 00:00:00 2001 From: abrichr Date: Sat, 24 Oct 2026 21:55:28 -0400 Subject: [PATCH 07/13] duplicate check: review fixes, one resolution for every path, REST opt-in CI: resolution no longer reads attributes a verifier double lacks (unit shards), the CAPABILITIES row states what was measured (copy test), and the public artifact inventory is regenerated (lint). Fail-closed reads: a REST body with any error field or no data list, a partial record birth date (now no date, so a name match is close), a record with no id, and a next-page link to another host are all unreadable instead of a silent none. One resolution: the deployment switch is gone; the program's candidate_source (part of the qualified program) and the run's own verifier decide on every path, and a qualify hook can't set the source. FHIR is the only automatic channel; OpenEMR's REST adapter needs candidate_source: record. candidate_record_fields maps a program's parameter names. The SQL adapter uses MySQL %s parameters and a bound wildcard. Recall: a nickname or a last-name typo with a mistyped date of birth now stops. Extended stream (694 items, 241 duplicates): misses 24 (all name change plus move), false stops 2 (0.3 per 100); screen 42 and 252. Co-Authored-By: Claude Opus 5.5 --- CAPABILITIES.md | 2 +- llms-full.txt | 2 +- packages/flow/benchmark/dup_check/README.md | 246 ++- packages/flow/benchmark/dup_check/measure.py | 27 +- .../dup_check/results/seed7-500.json | 1423 +++++++++------- .../dup_check/results/soak-seed7-55.json | 1501 +++++++++-------- packages/flow/docs/DUPLICATE_CHECK.md | 100 +- packages/flow/openadapt_flow/deployment.py | 10 - packages/flow/openadapt_flow/ir.py | 5 + .../flow/openadapt_flow/qualify/campaign.py | 7 +- .../runtime/checks/duplicate_record.py | 200 ++- .../flow/openadapt_flow/runtime/replayer.py | 56 +- packages/flow/public-artifacts.json | 25 + .../flow/tests/test_duplicate_record_check.py | 96 +- .../flow/tests/test_duplicate_source_paths.py | 92 +- 15 files changed, 2218 insertions(+), 1574 deletions(-) diff --git a/CAPABILITIES.md b/CAPABILITIES.md index 3c92d7ab2..f122632a7 100644 --- a/CAPABILITIES.md +++ b/CAPABILITIES.md @@ -229,7 +229,7 @@ improvising or retrying when something is off. | Repair by default: once a repair agent is configured, every stop that wrote nothing goes to it without anyone starting it; a stop after a possible write goes to reconciliation; every stop is a row in a ledger with the funnel from stop to resumed item | On main ([#1360](https://github.com/OpenAdaptAI/OpenAdapt/pull/1360)) | Synthetic (MockMed, 22 drift modes, real Claude agent: 6 of 21 repairable stops resumed and verified, 0 wrong saves in 66 runs); routing over recorded OpenEMR stops; live openIMIS relabel: 3 of 3 reached repair, 0 resumed; silent-saves 1.1.0 UI-change blocks: 18 of 28 items that always stopped now correct, 0 wrong | `run` (on by default, `--no-auto-repair`), `repair:` in `deploy.yaml`, [benchmark/auto_repair](packages/flow/benchmark/auto_repair/README.md) | | Repair a relabeled save button: prove from the recorded identity hash that only the label changed (same control id, kind and form), re-qualify the fix on the new release, and have a person admit the certified copy afresh, since a save button's identity is part of the record check | On main | Real app (local OpenEMR, synthetic data): after the save button was relabeled, the stopped item re-ran and saved once after 8.3 operator minutes and 763 s of re-qualification; 896 s from stop to done ([#1513](https://github.com/OpenAdaptAI/OpenAdapt/pull/1513)) | `openadapt_flow/repair/relabel.py`, [SELF_REPAIR.md](packages/flow/docs/SELF_REPAIR.md#a-relabeled-control-proof-that-only-the-label-changed) | | A duplicate check that reads the app's match list (`no_duplicate_candidate`): stop only on a listed record with the item's last name or date of birth, fail closed on an unreadable or unloaded list | On main | Real app (local OpenEMR, synthetic data): false stops per 100 items 70.4 before, 16.4 after; 0 silent wrongs in 30 saves ([#1513](https://github.com/OpenAdaptAI/OpenAdapt/pull/1513)) | [DUPLICATE_CHECK.md](packages/flow/docs/DUPLICATE_CHECK.md) | -| A duplicate check that asks the system of record (FHIR `Patient` search or OpenEMR's patient API, every page; SQL as a documented fallback): stop on an exact or close match, proceed otherwise, fail closed on any read problem. The default when `effects` reads FHIR or OpenEMR's patient API | Open PR | Offline on the soak's seed-7 stream with planted true duplicates: false stops per 100 items 37.3 (screen) against 0.3 (record) on 676 items; true duplicates missed: screen 30, record 24 of 223, every record miss a name change plus a move that shares no name, contact or date with the chart. Not yet run on live OpenEMR | [DUPLICATE_CHECK.md](packages/flow/docs/DUPLICATE_CHECK.md) | +| A duplicate check that asks the system of record (generic FHIR R4 `Patient` search; OpenEMR REST and SQL adapters): every page read, stop on an exact or close match, proceed otherwise, fail closed on any read problem. One source for qualification, runs and resume; a program can pin it | On main | Offline, the soak's seed-7 stream with planted true duplicates (694 items, 241 duplicates): false stops per 100 items 36.3 (screen) and 0.3 (record); duplicates missed 42 (screen) and 24 (record). The record's 24 are a name change plus a move, sharing only a first name or only a date of birth with the chart | [DUPLICATE_CHECK.md](packages/flow/docs/DUPLICATE_CHECK.md) | | A switched-off `problems_only` gate asks a person before the save instead of stopping the item | On main | Synthetic; real app in [#1513](https://github.com/OpenAdaptAI/OpenAdapt/pull/1513) | [APPROVAL_MODES.md](packages/flow/docs/APPROVAL_MODES.md#when-the-mode-turns-itself-off) | | Fleet drift patches: a verified fix with no patient data, shared across sites under policy | Released, local reference hub | Simulator | `openadapt-flow fleet`, [FLEET_DRIFT_PATCHES.md](packages/flow/docs/FLEET_DRIFT_PATCHES.md) | diff --git a/llms-full.txt b/llms-full.txt index 8fab2d684..afacfb6ed 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -626,7 +626,7 @@ improvising or retrying when something is off. | Repair by default: once a repair agent is configured, every stop that wrote nothing goes to it without anyone starting it; a stop after a possible write goes to reconciliation; every stop is a row in a ledger with the funnel from stop to resumed item | On main ([#1360](https://github.com/OpenAdaptAI/OpenAdapt/pull/1360)) | Synthetic (MockMed, 22 drift modes, real Claude agent: 6 of 21 repairable stops resumed and verified, 0 wrong saves in 66 runs); routing over recorded OpenEMR stops; live openIMIS relabel: 3 of 3 reached repair, 0 resumed; silent-saves 1.1.0 UI-change blocks: 18 of 28 items that always stopped now correct, 0 wrong | `run` (on by default, `--no-auto-repair`), `repair:` in `deploy.yaml`, [benchmark/auto_repair](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/benchmark/auto_repair/README.md) | | Repair a relabeled save button: prove from the recorded identity hash that only the label changed (same control id, kind and form), re-qualify the fix on the new release, and have a person admit the certified copy afresh, since a save button's identity is part of the record check | On main | Real app (local OpenEMR, synthetic data): after the save button was relabeled, the stopped item re-ran and saved once after 8.3 operator minutes and 763 s of re-qualification; 896 s from stop to done ([#1513](https://github.com/OpenAdaptAI/OpenAdapt/pull/1513)) | `openadapt_flow/repair/relabel.py`, [SELF_REPAIR.md](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/SELF_REPAIR.md#a-relabeled-control-proof-that-only-the-label-changed) | | A duplicate check that reads the app's match list (`no_duplicate_candidate`): stop only on a listed record with the item's last name or date of birth, fail closed on an unreadable or unloaded list | On main | Real app (local OpenEMR, synthetic data): false stops per 100 items 70.4 before, 16.4 after; 0 silent wrongs in 30 saves ([#1513](https://github.com/OpenAdaptAI/OpenAdapt/pull/1513)) | [DUPLICATE_CHECK.md](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/DUPLICATE_CHECK.md) | -| A duplicate check that asks the system of record (FHIR `Patient` search or OpenEMR's patient API, every page; SQL as a documented fallback): stop on an exact or close match, proceed otherwise, fail closed on any read problem. The default when `effects` reads FHIR or OpenEMR's patient API | Open PR | Offline on the soak's seed-7 stream with planted true duplicates: false stops per 100 items 37.3 (screen) against 0.3 (record) on 676 items; true duplicates missed: screen 30, record 24 of 223, every record miss a name change plus a move that shares no name, contact or date with the chart. Not yet run on live OpenEMR | [DUPLICATE_CHECK.md](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/DUPLICATE_CHECK.md) | +| A duplicate check that asks the system of record (generic FHIR R4 `Patient` search; OpenEMR REST and SQL adapters): every page read, stop on an exact or close match, proceed otherwise, fail closed on any read problem. One source for qualification, runs and resume; a program can pin it | On main | Offline, the soak's seed-7 stream with planted true duplicates (694 items, 241 duplicates): false stops per 100 items 36.3 (screen) and 0.3 (record); duplicates missed 42 (screen) and 24 (record). The record's 24 are a name change plus a move, sharing only a first name or only a date of birth with the chart | [DUPLICATE_CHECK.md](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/DUPLICATE_CHECK.md) | | A switched-off `problems_only` gate asks a person before the save instead of stopping the item | On main | Synthetic; real app in [#1513](https://github.com/OpenAdaptAI/OpenAdapt/pull/1513) | [APPROVAL_MODES.md](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/APPROVAL_MODES.md#when-the-mode-turns-itself-off) | | Fleet drift patches: a verified fix with no patient data, shared across sites under policy | Released, local reference hub | Simulator | `openadapt-flow fleet`, [FLEET_DRIFT_PATCHES.md](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/FLEET_DRIFT_PATCHES.md) | diff --git a/packages/flow/benchmark/dup_check/README.md b/packages/flow/benchmark/dup_check/README.md index 750077343..9953c07d6 100644 --- a/packages/flow/benchmark/dup_check/README.md +++ b/packages/flow/benchmark/dup_check/README.md @@ -10,171 +10,113 @@ rule, on the same stream and the same patient table. python -m benchmark.dup_check.measure # writes results/*.json ``` -No app, no model calls. `measure.py` says exactly what each check sees. - -## The streams - -- `soak-seed7-55`: the 55 items the soak ran (`lifecycle/results/production-2026-10-24/after/workload.jsonl`), with its 12 existing charts. -- `seed7-500`: 500 items from the same generator and seed (`lifecycle.workload.generate(7, 500, 10)`). - -Planted into each, for every existing chart, are true duplicates the soak -lacks: -- the same person under a married name; -- the same person under a hyphenated name; -- the same person with day and month swapped (when that is a valid date). - -Three more kinds were added after the record check's rules were written, as -a held-out set: a typo in the first name, a typo in the last name, and a -married name plus a mistyped day of birth. - -Each item meets the existing charts, every earlier item's right outcome from -the soak's truth, and another clerk's chart for `concurrent_same`. +No app, no model calls. `measure.py` says exactly what each check sees. The +record check reads an in-memory table with FHIR search semantics +(`TableSource`): a case-insensitive name prefix and an exact birth date. ## The exact numbers A miss is a second chart for a patient who already has one. -**Test set (extended stream):** 676 items, 440 new patients and 223 true -duplicates. -- 47 duplicates come from the soak's own seed-7 generator: 12 existing - patients referred again, 19 resends, 10 same people with a mistyped date of - birth, and 6 registered by another clerk moments before. -- 176 are planted variants of the existing patients. - -The first report's "12 of 110" was the same stream before the second and -third rounds of planted kinds were added. Each round grew the set; the misses -below are on the full 223. - -| | screen check | record check | -|---|---:|---:| -| missed, of 223 | 30 | 24 | -| missed, of the 47 from the generator | 0 | 0 | -| missed, of the 176 planted | 30 | 24 | -| false stops, of 440 new patients | 252 | 2 | - -The record check's 24 misses, all a known limit (uncatchable without extra -data): -- 12 `limit_married_moved_dob_typo`: a new last name, a new address, phone - and email, and a mistyped day of birth; -- 12 `limit_new_name_moved_same_dob`: new first and last names and new - contact details, with the same date of birth. - -The referral shares nothing with the chart except, in the second kind, a date -of birth that thousands of patients share. Catching them needs an MRN, an -insurance member id, or a person. The screen stops the second kind only -because it stops every new patient who shares a date of birth with anyone. - -The screen check's 30 misses: -- 12 `heldout_married_dob_typo`; -- 12 `limit_married_moved_dob_typo`; -- 6 `r2_prior_name_moved_dob_typo`. - -The record check catches the first and third of these. - -The soak's own 55 items with the same planting: 231 items, 184 true -duplicates (8 from the generator, 176 planted). Screen: 30 missed, 10 false -stops. Record: 24 missed (the same two limit kinds), 0 false stops. - -The planted kinds not in the table below (all caught by both checks): -- `planted_married`: a married name, everything else the same; -- `heldout_first_typo` and `heldout_last_typo`: one transposed letter; -- 12 of each. - -## Results (2026-10-24, second round) - -The first version of the record check missed 12 of 110 planted duplicates, -all of one kind: a married name with a mistyped day of birth. In every one, -the phone, street and email matched the chart exactly. So the second round -adds: -- searches by first name, phone and email; -- a nickname table; -- swapped first and last names; -- the chart's prior names (OpenEMR's `birth_fname`/`birth_lname`, or FHIR - `name.use` old or maiden); -- contact details (phone, email, street) as tie-breakers for a mistyped date - of birth. - -It also adds the kinds those rules target (`r2_`). Two `limit_` kinds share -nothing with the chart but the person, so no rule here can catch them. The -`r2_` rules were written knowing the `r2_` kinds, so those catches are not a -blind test. - -Then production-lifecycle named six cases that must stop. Each is planted as -a `pl_` kind: -- a married name and a new address; -- a mistyped day of birth; -- a second surname ("Garcia Lopez" against "Garcia"); -- an initial ("J." for the first name); -- a crossed name; -- both names misspelled with the same phone and email. - -The initial was the one gap, and a rule was added for it. - -Extended stream (500 seed-7 items, plus the planted and extra charts): 676 -items, 440 new charts, 223 true duplicates. - -| check | false stops (per 100 items) | missed duplicates | -|---|---:|---:| -| screen (`no_duplicate_candidate` on the list) | 252 (37.3) | 30 | -| record, first version | 2 (0.3) | 72 | -| record, now | 2 (0.3) | 24 | - -The soak's own 55 items, with the same planting (231 items, 184 true -duplicates): screen 10 false stops (4.3 per 100), 30 missed; record 0 false -stops, 24 missed. - -Every kind, extended stream (planted count; missed by screen, record v1, -record now): +**Test set:** 500 items from the soak's seed-7 generator +(`lifecycle.workload.generate(7, 500, 10)`), with planted duplicates and extra +charts: 694 items, 440 new patients and 241 true duplicates. +- 47 duplicates come from the generator itself: 12 existing patients referred + again, 19 resends, 10 same people with a mistyped date of birth, and 6 + registered by another clerk moments before. +- 194 are planted variants of existing patients. + +The set grew in four rounds. The first report's "12 of 110" was round one; +every number here is on the full 241. + +| | screen check | record check, first version | record check, now | +|---|---:|---:|---:| +| missed, of 241 | 42 | 90 | 24 | +| missed, of the 47 from the generator | 0 | 0 | 0 | +| false stops, of 440 new patients | 252 (36.3 per 100 items) | 2 (0.3) | 2 (0.3) | + +The soak's own 55 items, with the same planting (249 items, 202 true +duplicates): screen 42 missed and 10 false stops; record 24 missed and 0 +false stops. + +**Every kind** (planted count; missed by the screen, by the first version of +the record check, and by the record check now): | kind | planted | screen | v1 | now | what catches it now | |---|---:|---:|---:|---:|---| -| married name + mistyped day (`heldout_married_dob_typo`) | 12 | 12 | 12 | 0 | first name, a near date of birth, a shared phone, street or email | +| from the generator (`dup_existing`, `dup_resend`, `ambiguous_same_person`, `concurrent_same`) | 47 | 0 | 0 | 0 | exact match, or same name with the date one field off | +| married name (`planted_married`) | 12 | 0 | 0 | 0 | same first name and date of birth | | married name, new address (`pl_married_moved`) | 12 | 0 | 0 | 0 | same first name and date of birth | -| same name, mistyped day (`pl_ambiguous_same_person`, `ambiguous_same_person`) | 22 | 0 | 0 | 0 | same name, date of birth one field off | -| day and month swapped (`planted_swapped_dob`) | 3 | 0 | 0 | 0 | same name, swapped date | -| second surname (`pl_second_surname`) | 6 | 0 | 0 | 0 | a shared surname part, same first name and date of birth | +| married name + mistyped day (`heldout_married_dob_typo`) | 12 | 12 | 12 | 0 | first name, a near date, a shared phone, street or email | | hyphenated name (`planted_hyphenated`) | 12 | 0 | 0 | 0 | a shared surname part | +| second surname, "Garcia Lopez" for "Garcia" (`pl_second_surname`) | 6 | 0 | 0 | 0 | a shared surname part | +| day and month swapped (`planted_swapped_dob`) | 3 | 0 | 0 | 0 | same name, swapped date | +| same name, mistyped day (`pl_ambiguous_same_person`) | 12 | 0 | 0 | 0 | same name, date one field off | +| year digits transposed (`r2_year_transposed`) | 11 | 0 | 0 | 0 | same name, date one field off | +| typo in the first name (`heldout_first_typo`) | 12 | 0 | 0 | 0 | a similar name, same date | +| typo in the last name (`heldout_last_typo`) | 12 | 0 | 0 | 0 | a similar name, same date | +| typo in the last name + mistyped day (`rv_last_typo_dob_typo`) | 12 | 12 | 12 | 0 | same first name, similar last name, a near date | | initial for the first name (`pl_initial`) | 12 | 0 | 12 | 0 | the initial rule | -| nickname (`r2_nickname`) | 6 | 0 | 6 | 0 | the nickname table | +| nickname, Bob for Robert (`r2_nickname`) | 6 | 0 | 6 | 0 | the nickname table | +| nickname + mistyped day (`rv_nickname_dob_typo`) | 6 | 0 | 6 | 0 | the nickname table, same last name, a near date | | first and last name swapped (`r2_name_swap`) | 12 | 0 | 12 | 0 | the crossed names | -| both names misspelled, same contact (`pl_both_typos_same_contact`) | 12 | 0 | 0 | 0 | similar names, same date of birth | +| both names misspelled, same contact (`pl_both_typos_same_contact`) | 12 | 0 | 0 | 0 | similar names, same date | | prior name on the chart, moved, mistyped day (`r2_prior_name_moved_dob_typo`) | 6 | 6 | 6 | 0 | the chart's birth name | -| year digits transposed (`r2_year_transposed`) | 11 | 0 | 0 | 0 | same name, date of birth one field off | -| married name, moved, mistyped day (`limit_married_moved_dob_typo`) | 12 | 12 | 12 | 12 | nothing in the referral: needs an MRN, an insurance id or a person | -| new first and last name, moved, same day (`limit_new_name_moved_same_dob`) | 12 | 0 | 12 | 12 | nothing in the referral; the screen stops it only because it stops every shared date of birth | - -Every match the record check makes is `close` or `exact`, and both stop. - -**The trade-off.** Misses fell from 72 to 24, and every remaining miss is a -`limit_` kind, while false stops stayed at 2 of 440 new charts. Both are -`shared_dob` items whose first name equals that of the chart with the same -birth date, which the married-name rule sends to a person on purpose. The -screen still stops `limit_new_name_moved_same_dob`, and the record check -doesn't: a full name change and a move, with nothing but the birth date in -common, looks like any new patient who shares a birthday. Stopping it would -mean stopping every shared date of birth, which is 33 of the screen's 252 -false stops. - -One rule was narrowed during this round. "Another name, one shared contact -detail and a near date of birth" stopped two new patients, because the -generator's phone pool has only 100 numbers (`555-01xx`). It now needs two -shared contact details for a near date of birth, or one for the same date. -That narrowing is tuned to this stream, and a real phone pool is far larger. +| married name, moved, mistyped day (`limit_married_moved_dob_typo`) | 12 | 12 | 12 | **12** | known limit | +| new first and last name, moved, same day (`limit_new_name_moved_same_dob`) | 12 | 0 | 12 | **12** | known limit | + +The rows add to 241. Every match the record check makes is `close` or +`exact`, and both stop the item for a person. + +**Known limit: 24 misses, uncatchable without extra data.** +- 12 are a new last name with a new address, phone and email, and a mistyped + day of birth. They share only the first name with the chart. +- 12 are new first and last names with new contact details. They share only + the date of birth, as hundreds of patients do. + +Catching them needs an MRN, an insurance member id, or a person. The screen +stops the second kind only because it stops every new patient who shares a +date of birth with anyone. Stopping on a shared date of birth alone would +bring back the screen's `shared_dob` false stops (33 here). + +**The trade-off.** +- Misses fell from 90 (first version) to 24, and all 24 are the known limit. +- False stops stayed at 2 of 440 new patients. Both are `shared_dob` items + whose first name equals that of the chart born that day, which the + married-name rule sends to a person on purpose. +- One rule was narrowed along the way. "Another name, one shared contact + detail and a near date of birth" stopped two new patients, because the + generator's phone pool has only 100 numbers (`555-01xx`). It now needs two + shared contact details for a near date, or one for the same date. That + narrowing is tuned to this stream. + +## How the set was built + +For every existing chart, `plant` inserts duplicates at seeded positions: +- round one: a married name, a hyphenated name and a swapped date, then the + held-out first-name typo, last-name typo, and married name plus a mistyped + day; +- round two (`r2_`): a crossed name, a transposed year, and nicknames and + prior names on extra charts; +- round three (`pl_`): the six cases production-lifecycle named; +- round four (`rv_`): the two the review reproduced. + +Each item meets the existing and extra charts, every earlier item's right +outcome from the soak's truth, and another clerk's chart for +`concurrent_same`. + +The rules for each round were written knowing that round's kinds, so only +round one's held-out kinds were a blind test. They are why the second round +exists. ## Limits -- Offline: the table has FHIR string-search semantics (a case-insensitive - last-name prefix, an exact birth date). Not yet run against live OpenEMR's - FHIR or REST search; that run needs the fixture slot (`dup-check-sor` in - the queue). The paging and fail-closed behavior is tested against a real - local HTTP server (`tests/test_duplicate_record_check.py`). -- The close-match rules were written with the soak's generator in view - (`ambiguous_same_person` changes the day of birth). Only the held-out kinds - are a test of rules not tuned to them. -- The search for prior names assumes OpenEMR's FHIR `family` search also - matches a patient's old names. The SQL fallback does (`birth_lname`); the - live run must confirm FHIR. `patient_history`'s previous names are not read - yet. -- One seed. The screen rule is the `no_duplicate_candidate` rule applied to - OpenEMR's listing as the soak describes it (any chart sharing the first - name, the last name or the date of birth); it is not a run of the browser. +- Offline. The channel adapters' paging and fail-closed behavior is tested + against a real local HTTP server (`tests/test_duplicate_record_check.py`), + not against OpenEMR. +- The search for prior names assumes a FHIR `family` search also matches a + patient's old names. The SQL adapter does (`birth_lname`). + `patient_history`'s previous names are not read. +- One seed. The screen rule is `no_duplicate_candidate` applied to OpenEMR's + listing as the soak describes it: any chart sharing the first name, the + last name or the date of birth. It is not a run of the browser. diff --git a/packages/flow/benchmark/dup_check/measure.py b/packages/flow/benchmark/dup_check/measure.py index 3c1612556..f3dc544eb 100644 --- a/packages/flow/benchmark/dup_check/measure.py +++ b/packages/flow/benchmark/dup_check/measure.py @@ -67,11 +67,8 @@ "pl_initial", "pl_both_typos_same_contact", "pl_ambiguous_same_person", - "pl_married_moved", - "pl_second_surname", - "pl_initial", - "pl_both_typos_same_contact", - "pl_ambiguous_same_person", + "rv_nickname_dob_typo", + "rv_last_typo_dob_typo", "limit_married_moved_dob_typo", "limit_new_name_moved_same_dob", } @@ -248,6 +245,17 @@ def round_two(n: int, person: dict[str, str]) -> list[Item]: def typo(v: str) -> str: return v[:-2] + v[-1] + v[-2] if len(v) > 3 else v + "e" + # The review's reproduced gap (``rv_``): a last-name typo plus a day typo. + out.append( + Item( + f"rv-lt-{n}", + "rv_last_typo_dob_typo", + dict(person, lname=typo(person["lname"]), DOB=day_typo(person["DOB"])), + None, + [], + ) + ) + out += [ Item( f"pl-mm-{n}", @@ -329,6 +337,15 @@ def extra_existing( items.append( Item(f"r2-nk-{n}", "r2_nickname", dict(chart, fname=nick), None, []) ) + items.append( + Item( + f"rv-nd-{n}", + "rv_nickname_dob_typo", + dict(chart, fname=nick, DOB=day_typo(chart["DOB"])), + None, + [], + ) + ) for n in range(6): base = existing[(n + 5) % len(existing)] chart = dict( diff --git a/packages/flow/benchmark/dup_check/results/seed7-500.json b/packages/flow/benchmark/dup_check/results/seed7-500.json index 5211933d5..dc73843eb 100644 --- a/packages/flow/benchmark/dup_check/results/seed7-500.json +++ b/packages/flow/benchmark/dup_check/results/seed7-500.json @@ -1,5 +1,15 @@ { "rows": [ + { + "exception": "pl_married_moved", + "item": "pl-mm-11", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00001", @@ -33,11 +43,11 @@ "screen": "proceed" }, { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-9", + "exception": "heldout_last_typo", + "item": "held-l-9", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -109,14 +119,14 @@ "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-9", + "exception": "pl_ambiguous_same_person", + "item": "pl-as-8", "record": "close", "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "concurrent_other", @@ -209,12 +219,11 @@ "screen": "proceed" }, { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-6", + "exception": "r2_name_swap", + "item": "r2-sw-6", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" + "first and last name swapped" ], "right": "no chart: duplicate", "screen": "stop" @@ -224,7 +233,7 @@ "item": "ref-00024", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -346,11 +355,11 @@ "screen": "stop" }, { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-0", + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-0", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -364,12 +373,11 @@ "screen": "proceed" }, { - "exception": "planted_married", - "item": "plant-m-5", + "exception": "pl_ambiguous_same_person", + "item": "pl-as-4", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -436,10 +444,12 @@ "screen": "proceed" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-1", - "record": "none", - "record_why": [], + "exception": "pl_ambiguous_same_person", + "item": "pl-as-1", + "record": "close", + "record_why": [ + "same or similar name, a mistyped date of birth" + ], "right": "no chart: duplicate", "screen": "stop" }, @@ -451,6 +461,16 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "r2_name_swap", + "item": "r2-sw-11", + "record": "close", + "record_why": [ + "first and last name swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00048", @@ -468,8 +488,8 @@ "screen": "proceed" }, { - "exception": "pl_married_moved", - "item": "pl-mm-10", + "exception": "planted_married", + "item": "plant-m-10", "record": "close", "record_why": [ "same date of birth and first name, another last name" @@ -478,11 +498,11 @@ "screen": "stop" }, { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-1", + "exception": "pl_initial", + "item": "pl-in-1", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -541,22 +561,21 @@ "screen": "stop" }, { - "exception": "r2_name_swap", - "item": "r2-sw-11", + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-10", "record": "close", "record_why": [ - "first and last name swapped" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-7", + "exception": "pl_ambiguous_same_person", + "item": "pl-as-6", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -586,33 +605,28 @@ "screen": "proceed" }, { - "exception": "pl_initial", - "item": "pl-in-11", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-10", + "record": "none", + "record_why": [], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { - "exception": "heldout_first_typo", - "item": "held-f-6", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-5", + "record": "none", + "record_why": [], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-2", + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-2", "record": "none", "record_why": [], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "failed_save", @@ -623,43 +637,44 @@ "screen": "proceed" }, { - "exception": "r2_name_swap", - "item": "r2-sw-1", + "exception": "heldout_married_dob_typo", + "item": "held-md-1", "record": "close", "record_why": [ - "first and last name swapped" + "same first name, another last name, same phone, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { - "exception": "pl_married_moved", - "item": "pl-mm-0", + "exception": "rv_last_typo_dob_typo", + "item": "rv-lt-0", "record": "close", "record_why": [ - "same date of birth and first name, another last name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { - "exception": "planted_hyphenated", - "item": "plant-h-6", + "exception": "pl_ambiguous_same_person", + "item": "pl-as-5", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-2", - "record": "none", - "record_why": [], + "exception": "pl_ambiguous_same_person", + "item": "pl-as-2", + "record": "close", + "record_why": [ + "same or similar name, a mistyped date of birth" + ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "clean", @@ -670,14 +685,14 @@ "screen": "proceed" }, { - "exception": "pl_initial", - "item": "pl-in-1", + "exception": "rv_last_typo_dob_typo", + "item": "rv-lt-1", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "clean", @@ -688,14 +703,14 @@ "screen": "proceed" }, { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-5", + "exception": "rv_last_typo_dob_typo", + "item": "rv-lt-5", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "clean", @@ -741,14 +756,15 @@ "screen": "proceed" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-4", + "exception": "heldout_first_typo", + "item": "held-f-4", "record": "close", "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "clean", @@ -759,11 +775,11 @@ "screen": "proceed" }, { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-7", + "exception": "r2_name_swap", + "item": "r2-sw-7", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "first and last name swapped" ], "right": "no chart: duplicate", "screen": "stop" @@ -777,10 +793,12 @@ "screen": "stop" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-8", - "record": "none", - "record_why": [], + "exception": "r2_year_transposed", + "item": "r2-ty-8", + "record": "close", + "record_why": [ + "same or similar name, a mistyped date of birth" + ], "right": "no chart: duplicate", "screen": "stop" }, @@ -793,22 +811,19 @@ "screen": "proceed" }, { - "exception": "planted_hyphenated", - "item": "plant-h-5", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-4", + "record": "none", + "record_why": [], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { - "exception": "planted_hyphenated", - "item": "plant-h-1", + "exception": "planted_married", + "item": "plant-m-1", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" @@ -856,12 +871,15 @@ "screen": "proceed" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-3", - "record": "none", - "record_why": [], + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-3", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "heldout_married_dob_typo", @@ -881,6 +899,14 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-11", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, { "exception": "clean", "item": "ref-00075", @@ -897,6 +923,16 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "rv_last_typo_dob_typo", + "item": "rv-lt-11", + "record": "close", + "record_why": [ + "same or similar name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, { "exception": "clean", "item": "ref-00077", @@ -922,12 +958,15 @@ "screen": "proceed" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-5", - "record": "none", - "record_why": [], + "exception": "pl_married_moved", + "item": "pl-mm-5", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" + ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "common_surname", @@ -938,11 +977,11 @@ "screen": "stop" }, { - "exception": "planted_hyphenated", - "item": "plant-h-11", + "exception": "r2_year_transposed", + "item": "r2-ty-10", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -968,16 +1007,18 @@ "item": "ref-00083", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-11", - "record": "none", - "record_why": [], + "exception": "heldout_first_typo", + "item": "held-f-11", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], "right": "no chart: duplicate", "screen": "stop" }, @@ -990,14 +1031,16 @@ "screen": "stop" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-0", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { + "exception": "pl_ambiguous_same_person", + "item": "pl-as-0", + "record": "close", + "record_why": [ + "same or similar name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { "exception": "clean", "item": "ref-00085", "record": "none", @@ -1016,8 +1059,8 @@ "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-1", + "exception": "heldout_first_typo", + "item": "held-f-1", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -1044,11 +1087,11 @@ "screen": "stop" }, { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-2", + "exception": "pl_initial", + "item": "pl-in-2", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -1104,11 +1147,11 @@ "screen": "stop" }, { - "exception": "pl_married_moved", - "item": "pl-mm-2", + "exception": "r2_year_transposed", + "item": "r2-ty-2", "record": "close", "record_why": [ - "same date of birth and first name, another last name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -1124,12 +1167,12 @@ "screen": "stop" }, { - "exception": "planted_married", - "item": "plant-m-7", + "exception": "pl_initial", + "item": "pl-in-6", "record": "close", "record_why": [ "same date of birth and first name, another last name", - "same date of birth and first name, another last name" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -1161,14 +1204,14 @@ "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-10", + "exception": "pl_ambiguous_same_person", + "item": "pl-as-9", "record": "close", "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "clean", @@ -1211,11 +1254,12 @@ "screen": "proceed" }, { - "exception": "r2_name_swap", - "item": "r2-sw-4", + "exception": "heldout_last_typo", + "item": "held-l-4", "record": "close", "record_why": [ - "first and last name swapped" + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -1237,11 +1281,12 @@ "screen": "proceed" }, { - "exception": "r2_name_swap", - "item": "r2-sw-7", + "exception": "planted_married", + "item": "plant-m-7", "record": "close", "record_why": [ - "first and last name swapped" + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" @@ -1279,14 +1324,14 @@ "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-2", + "exception": "heldout_last_typo", + "item": "held-l-2", "record": "close", "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" + "same date of birth, a similar name" ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "dup_resend", @@ -1299,12 +1344,11 @@ "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-8", + "exception": "pl_ambiguous_same_person", + "item": "pl-as-7", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -1317,6 +1361,16 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-11", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00111", @@ -1352,10 +1406,12 @@ "screen": "proceed" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-9", - "record": "none", - "record_why": [], + "exception": "heldout_married_dob_typo", + "item": "held-md-9", + "record": "close", + "record_why": [ + "same first name, another last name, same phone, a mistyped date of birth" + ], "right": "no chart: duplicate", "screen": "proceed" }, @@ -1364,7 +1420,7 @@ "item": "ref-00115", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -1386,11 +1442,11 @@ "screen": "proceed" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-1", + "exception": "r2_name_swap", + "item": "r2-sw-1", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "first and last name swapped" ], "right": "no chart: duplicate", "screen": "stop" @@ -1420,11 +1476,12 @@ "screen": "proceed" }, { - "exception": "planted_hyphenated", - "item": "plant-h-9", + "exception": "pl_married_moved", + "item": "pl-mm-8", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" @@ -1438,12 +1495,14 @@ "screen": "proceed" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-8", - "record": "none", - "record_why": [], + "exception": "r2_name_swap", + "item": "r2-sw-8", + "record": "close", + "record_why": [ + "first and last name swapped" + ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "clean", @@ -1454,12 +1513,14 @@ "screen": "stop" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-11", - "record": "none", - "record_why": [], + "exception": "planted_hyphenated", + "item": "plant-h-11", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "clean", @@ -1470,14 +1531,12 @@ "screen": "proceed" }, { - "exception": "planted_married", - "item": "plant-m-2", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-1", + "record": "none", + "record_why": [], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "dup_resend", @@ -1562,18 +1621,19 @@ "screen": "stop" }, { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-4", + "exception": "pl_married_moved", + "item": "pl-mm-4", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-2", + "exception": "planted_hyphenated", + "item": "plant-h-2", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -1598,12 +1658,14 @@ "screen": "proceed" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-7", - "record": "none", - "record_why": [], + "exception": "r2_year_transposed", + "item": "r2-ty-7", + "record": "close", + "record_why": [ + "same or similar name, a mistyped date of birth" + ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "clean", @@ -1654,20 +1716,23 @@ "screen": "proceed" }, { - "exception": "planted_married", - "item": "plant-m-9", + "exception": "rv_last_typo_dob_typo", + "item": "rv-lt-8", "record": "close", "record_why": [ - "same date of birth and first name, another last name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-6", - "record": "none", - "record_why": [], + "exception": "pl_married_moved", + "item": "pl-mm-6", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" + ], "right": "no chart: duplicate", "screen": "stop" }, @@ -1688,8 +1753,8 @@ "screen": "stop" }, { - "exception": "pl_married_moved", - "item": "pl-mm-9", + "exception": "planted_married", + "item": "plant-m-9", "record": "close", "record_why": [ "same date of birth and first name, another last name" @@ -1714,22 +1779,21 @@ "screen": "proceed" }, { - "exception": "pl_married_moved", - "item": "pl-mm-11", + "exception": "pl_ambiguous_same_person", + "item": "pl-as-10", "record": "close", "record_why": [ - "same date of birth and first name, another last name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "pl_initial", - "item": "pl-in-4", + "exception": "r2_year_transposed", + "item": "r2-ty-4", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -1861,12 +1925,10 @@ "screen": "stop" }, { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-11", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-10", + "record": "none", + "record_why": [], "right": "no chart: duplicate", "screen": "stop" }, @@ -1903,12 +1965,12 @@ "screen": "proceed" }, { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-3", + "exception": "pl_married_moved", + "item": "pl-mm-3", "record": "close", "record_why": [ "same date of birth and first name, another last name", - "same date of birth, a similar name" + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" @@ -1954,11 +2016,11 @@ "screen": "stop" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-2", + "exception": "r2_name_swap", + "item": "r2-sw-2", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "first and last name swapped" ], "right": "no chart: duplicate", "screen": "stop" @@ -1968,7 +2030,7 @@ "item": "ref-00169", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -2010,14 +2072,14 @@ "screen": "stop" }, { - "exception": "pl_initial", - "item": "pl-in-2", + "exception": "rv_last_typo_dob_typo", + "item": "rv-lt-2", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "common_surname", @@ -2108,15 +2170,12 @@ "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-7", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-6", + "record": "none", + "record_why": [], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "ambiguous_new_person", @@ -2145,13 +2204,10 @@ "screen": "proceed" }, { - "exception": "planted_married", - "item": "plant-m-3", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" - ], + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-2", + "record": "none", + "record_why": [], "right": "no chart: duplicate", "screen": "stop" }, @@ -2164,20 +2220,22 @@ "screen": "proceed" }, { - "exception": "heldout_first_typo", - "item": "held-f-9", + "exception": "pl_initial", + "item": "pl-in-8", "record": "close", "record_why": [ + "same date of birth and first name, another last name", "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-9", + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-8", "record": "close", "record_why": [ + "same date of birth and first name, another last name", "same date of birth, a similar name" ], "right": "no chart: duplicate", @@ -2208,14 +2266,14 @@ "screen": "stop" }, { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-8", + "exception": "heldout_married_dob_typo", + "item": "held-md-8", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same first name, another last name, same phone, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "clean", @@ -2230,7 +2288,7 @@ "item": "ref-00193", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -2382,12 +2440,13 @@ "screen": "stop" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-7", + "exception": "planted_hyphenated", + "item": "plant-h-7", "record": "close", "record_why": [ - "same name, a mistyped date of birth" - ], + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], "right": "no chart: duplicate", "screen": "stop" }, @@ -2408,16 +2467,18 @@ "screen": "stop" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-1", - "record": "none", - "record_why": [], + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-1", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { - "exception": "pl_initial", - "item": "pl-in-9", + "exception": "planted_hyphenated", + "item": "plant-h-9", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -2434,8 +2495,8 @@ "screen": "stop" }, { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-0", + "exception": "pl_initial", + "item": "pl-in-0", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -2460,11 +2521,11 @@ "screen": "proceed" }, { - "exception": "pl_married_moved", - "item": "pl-mm-1", + "exception": "r2_year_transposed", + "item": "r2-ty-1", "record": "close", "record_why": [ - "same date of birth and first name, another last name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -2510,11 +2571,12 @@ "screen": "proceed" }, { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-3", + "exception": "pl_initial", + "item": "pl-in-3", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -2568,19 +2630,18 @@ "screen": "stop" }, { - "exception": "pl_initial", - "item": "pl-in-3", + "exception": "rv_last_typo_dob_typo", + "item": "rv-lt-3", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-9", + "exception": "heldout_first_typo", + "item": "held-f-9", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -2650,18 +2711,19 @@ "screen": "stop" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-8", + "exception": "planted_married", + "item": "plant-m-8", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-8", + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-7", "record": "close", "record_why": [ "same date of birth and first name, another last name", @@ -2695,16 +2757,19 @@ "screen": "stop" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-5", - "record": "none", - "record_why": [], + "exception": "pl_initial", + "item": "pl-in-5", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-1", + "exception": "planted_hyphenated", + "item": "plant-h-1", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -2787,14 +2852,12 @@ "screen": "proceed" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-7", - "record": "close", - "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" - ], + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-6", + "record": "none", + "record_why": [], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "concurrent_other", @@ -2813,11 +2876,12 @@ "screen": "stop" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-6", + "exception": "heldout_first_typo", + "item": "held-f-6", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -2887,8 +2951,8 @@ "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-10", + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-9", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -2945,12 +3009,11 @@ "screen": "proceed" }, { - "exception": "heldout_last_typo", - "item": "held-l-4", + "exception": "planted_swapped_dob", + "item": "plant-s-4", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" + "same name, day and month swapped" ], "right": "no chart: duplicate", "screen": "stop" @@ -2995,6 +3058,16 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "heldout_last_typo", + "item": "held-l-11", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00268", @@ -3044,13 +3117,10 @@ "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-6", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-5", + "record": "none", + "record_why": [], "right": "no chart: duplicate", "screen": "stop" }, @@ -3071,10 +3141,12 @@ "screen": "stop" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-3", - "record": "none", - "record_why": [], + "exception": "pl_ambiguous_same_person", + "item": "pl-as-3", + "record": "close", + "record_why": [ + "same or similar name, a mistyped date of birth" + ], "right": "no chart: duplicate", "screen": "stop" }, @@ -3095,22 +3167,19 @@ "screen": "stop" }, { - "exception": "planted_hyphenated", - "item": "plant-h-2", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-1", + "record": "none", + "record_why": [], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "pl_married_moved", - "item": "pl-mm-3", + "exception": "r2_year_transposed", + "item": "r2-ty-3", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -3132,14 +3201,15 @@ "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-5", + "exception": "planted_hyphenated", + "item": "plant-h-5", "record": "close", "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "missing_optional", @@ -3149,6 +3219,14 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-11", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00281", @@ -3246,8 +3324,8 @@ "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-3", + "exception": "heldout_first_typo", + "item": "held-f-3", "record": "close", "record_why": [ "same date of birth and first name, another last name", @@ -3267,14 +3345,14 @@ "screen": "stop" }, { - "exception": "r2_name_swap", - "item": "r2-sw-2", + "exception": "heldout_married_dob_typo", + "item": "held-md-2", "record": "close", "record_why": [ - "first and last name swapped" + "same first name, another last name, same phone, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "clean", @@ -3305,7 +3383,7 @@ "item": "ref-00297", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -3338,21 +3416,21 @@ "screen": "proceed" }, { - "exception": "planted_hyphenated", - "item": "plant-h-10", + "exception": "rv_last_typo_dob_typo", + "item": "rv-lt-9", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "ambiguous_same_person", "item": "ref-00301", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -3366,8 +3444,8 @@ "screen": "stop" }, { - "exception": "pl_initial", - "item": "pl-in-8", + "exception": "heldout_first_typo", + "item": "held-f-8", "record": "close", "record_why": [ "same date of birth and first name, another last name", @@ -3385,22 +3463,22 @@ "screen": "stop" }, { - "exception": "pl_initial", - "item": "pl-in-6", + "exception": "heldout_married_dob_typo", + "item": "held-md-6", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" + "same first name, another last name, same phone, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { - "exception": "r2_name_swap", - "item": "r2-sw-5", + "exception": "heldout_first_typo", + "item": "held-f-5", "record": "close", "record_why": [ - "first and last name swapped" + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -3446,10 +3524,12 @@ "screen": "stop" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-6", - "record": "none", - "record_why": [], + "exception": "rv_last_typo_dob_typo", + "item": "rv-lt-6", + "record": "close", + "record_why": [ + "same or similar name, a mistyped date of birth" + ], "right": "no chart: duplicate", "screen": "proceed" }, @@ -3462,15 +3542,14 @@ "screen": "stop" }, { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-4", + "exception": "rv_last_typo_dob_typo", + "item": "rv-lt-4", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "concurrent_other", @@ -3489,11 +3568,12 @@ "screen": "proceed" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-5", + "exception": "heldout_last_typo", + "item": "held-l-5", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -3515,11 +3595,11 @@ "screen": "stop" }, { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-10", + "exception": "heldout_first_typo", + "item": "held-f-10", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -3533,21 +3613,22 @@ "screen": "proceed" }, { - "exception": "planted_married", - "item": "plant-m-11", + "exception": "r2_name_swap", + "item": "r2-sw-10", "record": "close", "record_why": [ - "same date of birth and first name, another last name" + "first and last name swapped" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_swapped_dob", - "item": "plant-s-4", + "exception": "planted_married", + "item": "plant-m-4", "record": "close", "record_why": [ - "same name, day and month swapped" + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" @@ -3577,34 +3658,38 @@ "screen": "stop" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-10", - "record": "none", - "record_why": [], + "exception": "heldout_last_typo", + "item": "held-l-10", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-11", + "exception": "pl_initial", + "item": "pl-in-10", "record": "close", "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" + "same date of birth, a similar name" ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-10", - "record": "none", - "record_why": [], + "exception": "heldout_married_dob_typo", + "item": "held-md-10", + "record": "close", + "record_why": [ + "same first name, another last name, same phone, a mistyped date of birth" + ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-8", + "exception": "heldout_last_typo", + "item": "held-l-8", "record": "close", "record_why": [ "same date of birth and first name, another last name", @@ -3754,8 +3839,8 @@ "screen": "proceed" }, { - "exception": "pl_initial", - "item": "pl-in-7", + "exception": "heldout_last_typo", + "item": "held-l-7", "record": "close", "record_why": [ "same date of birth and first name, another last name", @@ -3805,48 +3890,50 @@ "screen": "stop" }, { - "exception": "pl_married_moved", - "item": "pl-mm-6", + "exception": "heldout_last_typo", + "item": "held-l-6", "record": "close", "record_why": [ "same date of birth and first name, another last name", - "same date of birth and first name, another last name" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-3", + "exception": "r2_name_swap", + "item": "r2-sw-3", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "first and last name swapped" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "pl_married_moved", - "item": "pl-mm-7", + "exception": "heldout_first_typo", + "item": "held-f-7", "record": "close", "record_why": [ "same date of birth and first name, another last name", - "same date of birth and first name, another last name" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-9", - "record": "none", - "record_why": [], + "exception": "r2_name_swap", + "item": "r2-sw-9", + "record": "close", + "record_why": [ + "first and last name swapped" + ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_married", - "item": "plant-m-8", + "exception": "pl_married_moved", + "item": "pl-mm-7", "record": "close", "record_why": [ "same date of birth and first name, another last name", @@ -3904,11 +3991,11 @@ "screen": "stop" }, { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-11", + "exception": "planted_married", + "item": "plant-m-11", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" @@ -3946,12 +4033,12 @@ "screen": "proceed" }, { - "exception": "pl_married_moved", - "item": "pl-mm-8", + "exception": "planted_hyphenated", + "item": "plant-h-8", "record": "close", "record_why": [ "same date of birth and first name, another last name", - "same date of birth and first name, another last name" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -4005,11 +4092,11 @@ "screen": "stop" }, { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-2", + "exception": "pl_married_moved", + "item": "pl-mm-2", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" @@ -4089,12 +4176,10 @@ "screen": "proceed" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-8", - "record": "close", - "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" - ], + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-7", + "record": "none", + "record_why": [], "right": "no chart: duplicate", "screen": "proceed" }, @@ -4131,8 +4216,8 @@ "screen": "stop" }, { - "exception": "pl_initial", - "item": "pl-in-10", + "exception": "planted_hyphenated", + "item": "plant-h-10", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -4161,7 +4246,7 @@ "item": "r2-ty-0", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -4279,41 +4364,45 @@ "screen": "stop" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-4", - "record": "none", - "record_why": [], + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-4", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-7", - "record": "none", - "record_why": [], + "exception": "rv_last_typo_dob_typo", + "item": "rv-lt-7", + "record": "close", + "record_why": [ + "same or similar name, a mistyped date of birth" + ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { - "exception": "pl_married_moved", - "item": "pl-mm-4", + "exception": "r2_name_swap", + "item": "r2-sw-4", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" + "first and last name swapped" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-11", + "exception": "rv_last_typo_dob_typo", + "item": "rv-lt-10", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "common_surname", @@ -4364,11 +4453,11 @@ "screen": "stop" }, { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-1", + "exception": "pl_married_moved", + "item": "pl-mm-1", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" @@ -4414,11 +4503,12 @@ "screen": "stop" }, { - "exception": "r2_name_swap", - "item": "r2-sw-6", + "exception": "planted_hyphenated", + "item": "plant-h-6", "record": "close", "record_why": [ - "first and last name swapped" + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -4472,14 +4562,12 @@ "screen": "stop" }, { - "exception": "r2_name_swap", - "item": "r2-sw-9", - "record": "close", - "record_why": [ - "first and last name swapped" - ], + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-8", + "record": "none", + "record_why": [], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "common_surname", @@ -4511,12 +4599,11 @@ "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-3", + "exception": "planted_swapped_dob", + "item": "plant-s-3", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" + "same name, day and month swapped" ], "right": "no chart: duplicate", "screen": "stop" @@ -4530,11 +4617,11 @@ "screen": "stop" }, { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-6", + "exception": "r2_year_transposed", + "item": "r2-ty-6", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -4552,17 +4639,17 @@ "item": "ref-00405", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-2", + "exception": "planted_married", + "item": "plant-m-2", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" @@ -4592,12 +4679,11 @@ "screen": "stop" }, { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-5", + "exception": "r2_year_transposed", + "item": "r2-ty-5", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -4643,8 +4729,8 @@ "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-4", + "exception": "planted_hyphenated", + "item": "plant-h-4", "record": "close", "record_why": [ "same date of birth and first name, another last name", @@ -4670,11 +4756,19 @@ "screen": "stop" }, { - "exception": "r2_name_swap", - "item": "r2-sw-10", + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-9", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "pl_ambiguous_same_person", + "item": "pl-as-11", "record": "close", "record_why": [ - "first and last name swapped" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -4696,12 +4790,10 @@ "screen": "proceed" }, { - "exception": "planted_married", - "item": "plant-m-1", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-0", + "record": "none", + "record_why": [], "right": "no chart: duplicate", "screen": "stop" }, @@ -4722,22 +4814,22 @@ "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-1", + "exception": "heldout_last_typo", + "item": "held-l-1", "record": "close", "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" + "same date of birth, a similar name" ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { - "exception": "planted_married", - "item": "plant-m-6", + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-5", "record": "close", "record_why": [ "same date of birth and first name, another last name", - "same date of birth and first name, another last name" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -4769,15 +4861,14 @@ "screen": "proceed" }, { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-7", + "exception": "heldout_married_dob_typo", + "item": "held-md-7", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" + "same first name, another last name, same phone, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "clean", @@ -4798,14 +4889,14 @@ "screen": "stop" }, { - "exception": "r2_name_swap", - "item": "r2-sw-3", + "exception": "heldout_married_dob_typo", + "item": "held-md-3", "record": "close", "record_why": [ - "first and last name swapped" + "same first name, another last name, same phone, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "clean", @@ -4816,8 +4907,8 @@ "screen": "stop" }, { - "exception": "planted_hyphenated", - "item": "plant-h-7", + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-6", "record": "close", "record_why": [ "same date of birth and first name, another last name", @@ -4889,11 +4980,11 @@ "screen": "proceed" }, { - "exception": "planted_married", - "item": "plant-m-10", + "exception": "r2_year_transposed", + "item": "r2-ty-9", "record": "close", "record_why": [ - "same date of birth and first name, another last name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -4907,12 +4998,12 @@ "screen": "stop" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-0", + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-0", "record": "none", "record_why": [], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "clean", @@ -4931,14 +5022,15 @@ "screen": "proceed" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-3", + "exception": "heldout_last_typo", + "item": "held-l-3", "record": "close", "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "clean", @@ -4953,7 +5045,7 @@ "item": "ref-00437", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -4966,6 +5058,16 @@ "right": "new chart", "screen": "stop" }, + { + "exception": "pl_initial", + "item": "pl-in-11", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00439", @@ -4991,12 +5093,15 @@ "screen": "stop" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-4", - "record": "none", - "record_why": [], + "exception": "pl_initial", + "item": "pl-in-4", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "clean", @@ -5007,15 +5112,14 @@ "screen": "stop" }, { - "exception": "pl_married_moved", - "item": "pl-mm-5", + "exception": "heldout_married_dob_typo", + "item": "held-md-5", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" + "same first name, another last name, same phone, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "shared_dob", @@ -5050,11 +5154,11 @@ "screen": "stop" }, { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-10", + "exception": "planted_swapped_dob", + "item": "plant-s-10", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same name, day and month swapped" ], "right": "no chart: duplicate", "screen": "stop" @@ -5142,13 +5246,10 @@ "screen": "stop" }, { - "exception": "planted_hyphenated", - "item": "plant-h-4", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-3", + "record": "none", + "record_why": [], "right": "no chart: duplicate", "screen": "stop" }, @@ -5161,8 +5262,8 @@ "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-10", + "exception": "pl_initial", + "item": "pl-in-9", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -5211,12 +5312,10 @@ "screen": "stop" }, { - "exception": "r2_name_swap", - "item": "r2-sw-8", - "record": "close", - "record_why": [ - "first and last name swapped" - ], + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-7", + "record": "none", + "record_why": [], "right": "no chart: duplicate", "screen": "stop" }, @@ -5237,8 +5336,8 @@ "screen": "stop" }, { - "exception": "planted_hyphenated", - "item": "plant-h-8", + "exception": "pl_initial", + "item": "pl-in-7", "record": "close", "record_why": [ "same date of birth and first name, another last name", @@ -5282,14 +5381,24 @@ "screen": "stop" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-4", + "exception": "heldout_married_dob_typo", + "item": "held-md-4", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same first name, another last name, same phone, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-11", + "record": "close", + "record_why": [ + "same first name, another last name, same phone, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "proceed" }, { "exception": "clean", @@ -5300,12 +5409,12 @@ "screen": "stop" }, { - "exception": "planted_hyphenated", - "item": "plant-h-3", + "exception": "planted_married", + "item": "plant-m-3", "record": "close", "record_why": [ "same date of birth and first name, another last name", - "same date of birth, a similar name" + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" @@ -5381,7 +5490,7 @@ "item": "ref-00478", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -5447,12 +5556,10 @@ "screen": "proceed" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-9", - "record": "close", - "record_why": [ - "same name, a mistyped date of birth" - ], + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-8", + "record": "none", + "record_why": [], "right": "no chart: duplicate", "screen": "stop" }, @@ -5465,15 +5572,12 @@ "screen": "stop" }, { - "exception": "planted_married", - "item": "plant-m-4", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" - ], + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-3", + "record": "none", + "record_why": [], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "clean", @@ -5556,11 +5660,11 @@ "screen": "stop" }, { - "exception": "planted_swapped_dob", - "item": "plant-s-10", + "exception": "pl_married_moved", + "item": "pl-mm-9", "record": "close", "record_why": [ - "same name, day and month swapped" + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" @@ -5590,12 +5694,10 @@ "screen": "proceed" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-10", - "record": "close", - "record_why": [ - "same name, a mistyped date of birth" - ], + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-9", + "record": "none", + "record_why": [], "right": "no chart: duplicate", "screen": "stop" }, @@ -5617,6 +5719,16 @@ "right": "no chart: duplicate", "screen": "stop" }, + { + "exception": "rv_nickname_dob_typo", + "item": "rv-nd-0", + "record": "close", + "record_why": [ + "same or similar name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "r2_nickname", "item": "r2-nk-1", @@ -5628,49 +5740,67 @@ "screen": "stop" }, { - "exception": "r2_nickname", - "item": "r2-nk-2", + "exception": "rv_nickname_dob_typo", + "item": "rv-nd-1", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "r2_nickname", - "item": "r2-nk-3", + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-4", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-3", "record": "close", "record_why": [ + "same date of birth and first name, another last name", "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-5", + "exception": "pl_married_moved", + "item": "pl-mm-10", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "r2_nickname", + "item": "r2-nk-2", "record": "close", "record_why": [ - "same date of birth and first name, another last name", "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_swapped_dob", - "item": "plant-s-3", + "exception": "rv_nickname_dob_typo", + "item": "rv-nd-2", "record": "close", "record_why": [ - "same name, day and month swapped" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-11", + "exception": "r2_nickname", + "item": "r2-nk-3", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -5678,6 +5808,16 @@ "right": "no chart: duplicate", "screen": "stop" }, + { + "exception": "rv_nickname_dob_typo", + "item": "rv-nd-3", + "record": "close", + "record_why": [ + "same or similar name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "r2_nickname", "item": "r2-nk-4", @@ -5688,6 +5828,27 @@ "right": "no chart: duplicate", "screen": "stop" }, + { + "exception": "rv_nickname_dob_typo", + "item": "rv-nd-4", + "record": "close", + "record_why": [ + "same or similar name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_married", + "item": "plant-m-6", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "r2_nickname", "item": "r2-nk-5", @@ -5698,6 +5859,26 @@ "right": "no chart: duplicate", "screen": "stop" }, + { + "exception": "pl_married_moved", + "item": "pl-mm-0", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "rv_nickname_dob_typo", + "item": "rv-nd-5", + "record": "close", + "record_why": [ + "same or similar name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "pl_second_surname", "item": "pl-ss-0", @@ -5739,14 +5920,15 @@ "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-6", + "exception": "planted_married", + "item": "plant-m-5", "record": "close", "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "pl_second_surname", @@ -5759,8 +5941,8 @@ "screen": "stop" }, { - "exception": "pl_initial", - "item": "pl-in-0", + "exception": "pl_second_surname", + "item": "pl-ss-5", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -5769,11 +5951,11 @@ "screen": "stop" }, { - "exception": "pl_second_surname", - "item": "pl-ss-5", + "exception": "r2_name_swap", + "item": "r2-sw-5", "record": "close", "record_why": [ - "same date of birth, a similar name" + "first and last name swapped" ], "right": "no chart: duplicate", "screen": "stop" @@ -5783,7 +5965,7 @@ "item": "r2-pn-0", "record": "close", "record_why": [ - "same name, a mistyped date of birth (a prior name on the chart)" + "same or similar name, a mistyped date of birth (a prior name on the chart)" ], "right": "no chart: duplicate", "screen": "proceed" @@ -5793,7 +5975,7 @@ "item": "r2-pn-1", "record": "close", "record_why": [ - "same name, a mistyped date of birth (a prior name on the chart)" + "same or similar name, a mistyped date of birth (a prior name on the chart)" ], "right": "no chart: duplicate", "screen": "proceed" @@ -5803,7 +5985,7 @@ "item": "r2-pn-2", "record": "close", "record_why": [ - "same name, a mistyped date of birth (a prior name on the chart)" + "same or similar name, a mistyped date of birth (a prior name on the chart)" ], "right": "no chart: duplicate", "screen": "proceed" @@ -5813,61 +5995,59 @@ "item": "r2-pn-3", "record": "close", "record_why": [ - "same name, a mistyped date of birth (a prior name on the chart)" + "same or similar name, a mistyped date of birth (a prior name on the chart)" ], "right": "no chart: duplicate", "screen": "proceed" }, { - "exception": "heldout_last_typo", - "item": "held-l-5", + "exception": "r2_prior_name_moved_dob_typo", + "item": "r2-pn-4", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" + "same or similar name, a mistyped date of birth (a prior name on the chart)" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { - "exception": "r2_prior_name_moved_dob_typo", - "item": "r2-pn-4", + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-2", "record": "close", "record_why": [ - "same name, a mistyped date of birth (a prior name on the chart)" + "same date of birth, a similar name" ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { - "exception": "r2_prior_name_moved_dob_typo", - "item": "r2-pn-5", + "exception": "heldout_first_typo", + "item": "held-f-2", "record": "close", "record_why": [ - "same name, a mistyped date of birth (a prior name on the chart)" + "same date of birth, a similar name" ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { - "exception": "pl_initial", - "item": "pl-in-5", + "exception": "r2_prior_name_moved_dob_typo", + "item": "r2-pn-5", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" + "same or similar name, a mistyped date of birth (a prior name on the chart)" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" } ], "summary": { - "items": 676, + "items": 694, "new_chart_items": 440, "record": { - "caught_duplicates": 199, + "caught_duplicates": 217, "decisions": { - "close": 164, + "close": 182, "exact": 37, "none": 462, "unreadable": 13 @@ -5884,7 +6064,7 @@ "missed_duplicates": 24 }, "screen": { - "caught_duplicates": 193, + "caught_duplicates": 199, "false_stops": 252, "false_stops_by_exception": { "ambiguous_new_person": 7, @@ -5899,14 +6079,15 @@ "shared_dob": 33, "slow_save": 5 }, - "false_stops_per_100_items": 37.3, + "false_stops_per_100_items": 36.3, "missed_by_exception": { "heldout_married_dob_typo": 12, "limit_married_moved_dob_typo": 12, - "r2_prior_name_moved_dob_typo": 6 + "r2_prior_name_moved_dob_typo": 6, + "rv_last_typo_dob_typo": 12 }, - "missed_duplicates": 30 + "missed_duplicates": 42 }, - "true_duplicates": 223 + "true_duplicates": 241 } } diff --git a/packages/flow/benchmark/dup_check/results/soak-seed7-55.json b/packages/flow/benchmark/dup_check/results/soak-seed7-55.json index 9afe406de..4191bb65f 100644 --- a/packages/flow/benchmark/dup_check/results/soak-seed7-55.json +++ b/packages/flow/benchmark/dup_check/results/soak-seed7-55.json @@ -8,6 +8,16 @@ "right": "new chart", "screen": "proceed" }, + { + "exception": "r2_name_swap", + "item": "r2-sw-11", + "record": "close", + "record_why": [ + "first and last name swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "clean", "item": "ref-00002", @@ -25,14 +35,12 @@ "screen": "proceed" }, { - "exception": "r2_name_swap", - "item": "r2-sw-11", - "record": "close", - "record_why": [ - "first and last name swapped" - ], + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-10", + "record": "none", + "record_why": [], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "clean", @@ -43,29 +51,30 @@ "screen": "proceed" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-0", + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-0", "record": "none", "record_why": [], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-8", + "exception": "planted_married", + "item": "plant-m-8", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "pl_initial", - "item": "pl-in-2", + "exception": "r2_year_transposed", + "item": "r2-ty-2", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -79,14 +88,14 @@ "screen": "stop" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-2", + "exception": "heldout_married_dob_typo", + "item": "held-md-2", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same first name, another last name, same phone, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "malformed_dob", @@ -97,28 +106,36 @@ "screen": "proceed" }, { - "exception": "heldout_first_typo", - "item": "held-f-2", + "exception": "planted_married", + "item": "plant-m-2", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "r2_name_swap", - "item": "r2-sw-6", + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-5", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "rv_last_typo_dob_typo", + "item": "rv-lt-1", "record": "close", "record_why": [ - "first and last name swapped" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { - "exception": "pl_initial", - "item": "pl-in-1", + "exception": "heldout_last_typo", + "item": "held-l-0", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -127,8 +144,8 @@ "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-0", + "exception": "heldout_first_typo", + "item": "held-f-1", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -137,28 +154,28 @@ "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-1", + "exception": "rv_last_typo_dob_typo", + "item": "rv-lt-8", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { - "exception": "planted_married", - "item": "plant-m-9", + "exception": "pl_initial", + "item": "pl-in-0", "record": "close", "record_why": [ - "same date of birth and first name, another last name" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-3", + "exception": "heldout_first_typo", + "item": "held-f-7", "record": "close", "record_why": [ "same date of birth and first name, another last name", @@ -168,8 +185,16 @@ "screen": "stop" }, { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-7", + "exception": "clean", + "item": "ref-00007", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-7", "record": "close", "record_why": [ "same date of birth and first name, another last name", @@ -179,8 +204,8 @@ "screen": "stop" }, { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-0", + "exception": "heldout_last_typo", + "item": "held-l-9", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -189,48 +214,50 @@ "screen": "stop" }, { - "exception": "pl_initial", - "item": "pl-in-7", + "exception": "r2_year_transposed", + "item": "r2-ty-10", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-9", + "exception": "pl_ambiguous_same_person", + "item": "pl-as-3", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-10", - "record": "none", - "record_why": [], + "exception": "pl_initial", + "item": "pl-in-6", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-4", + "exception": "planted_married", + "item": "plant-m-3", "record": "close", "record_why": [ "same date of birth and first name, another last name", - "same date of birth, a similar name" + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-7", + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-3", "record": "close", "record_why": [ "same date of birth and first name, another last name", @@ -240,16 +267,16 @@ "screen": "stop" }, { - "exception": "clean", - "item": "ref-00007", + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-5", "record": "none", "record_why": [], - "right": "new chart", - "screen": "proceed" + "right": "no chart: duplicate", + "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-4", + "exception": "heldout_last_typo", + "item": "held-l-5", "record": "close", "record_why": [ "same date of birth and first name, another last name", @@ -259,18 +286,18 @@ "screen": "stop" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-6", + "exception": "heldout_married_dob_typo", + "item": "held-md-1", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same first name, another last name, same phone, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { "exception": "pl_both_typos_same_contact", - "item": "pl-bt-5", + "item": "pl-bt-6", "record": "close", "record_why": [ "same date of birth and first name, another last name", @@ -280,29 +307,27 @@ "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-3", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", + "exception": "missing_required", + "item": "ref-00008", + "record": "unreadable", + "record_why": [], + "right": "no chart", "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-7", + "exception": "pl_initial", + "item": "pl-in-4", "record": "close", "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "r2_name_swap", - "item": "r2-sw-1", + "item": "r2-sw-8", "record": "close", "record_why": [ "first and last name swapped" @@ -311,32 +336,15 @@ "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-5", + "exception": "pl_initial", + "item": "pl-in-2", "record": "close", "record_why": [ - "same date of birth and first name, another last name", "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, - { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-8", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "missing_required", - "item": "ref-00008", - "record": "unreadable", - "record_why": [], - "right": "no chart", - "screen": "stop" - }, { "exception": "limit_married_moved_dob_typo", "item": "lim-mm-2", @@ -346,49 +354,46 @@ "screen": "proceed" }, { - "exception": "heldout_last_typo", - "item": "held-l-9", + "exception": "rv_last_typo_dob_typo", + "item": "rv-lt-11", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { - "exception": "planted_swapped_dob", - "item": "plant-s-10", + "exception": "pl_ambiguous_same_person", + "item": "pl-as-8", "record": "close", "record_why": [ - "same name, day and month swapped" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_hyphenated", - "item": "plant-h-3", + "exception": "pl_initial", + "item": "pl-in-9", "record": "close", "record_why": [ - "same date of birth and first name, another last name", "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_hyphenated", - "item": "plant-h-11", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-0", + "record": "none", + "record_why": [], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_married", - "item": "plant-m-1", + "exception": "pl_married_moved", + "item": "pl-mm-10", "record": "close", "record_why": [ "same date of birth and first name, another last name" @@ -397,19 +402,21 @@ "screen": "stop" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-10", - "record": "none", - "record_why": [], + "exception": "pl_initial", + "item": "pl-in-1", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-1", + "exception": "r2_name_swap", + "item": "r2-sw-10", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "first and last name swapped" ], "right": "no chart: duplicate", "screen": "stop" @@ -425,99 +432,88 @@ "screen": "proceed" }, { - "exception": "planted_swapped_dob", - "item": "plant-s-4", - "record": "close", - "record_why": [ - "same name, day and month swapped" - ], - "right": "no chart: duplicate", - "screen": "stop" + "exception": "clean", + "item": "ref-00009", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" }, { - "exception": "planted_married", - "item": "plant-m-4", + "exception": "pl_initial", + "item": "pl-in-3", "record": "close", "record_why": [ "same date of birth and first name, another last name", - "same date of birth and first name, another last name" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-11", + "exception": "clean", + "item": "ref-00010", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-11", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-5", + "exception": "heldout_married_dob_typo", + "item": "held-md-5", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same first name, another last name, same phone, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { - "exception": "planted_married", - "item": "plant-m-11", + "exception": "rv_last_typo_dob_typo", + "item": "rv-lt-10", "record": "close", "record_why": [ - "same date of birth and first name, another last name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-9", + "exception": "heldout_married_dob_typo", + "item": "held-md-9", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same first name, another last name, same phone, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { - "exception": "pl_initial", - "item": "pl-in-3", + "exception": "r2_year_transposed", + "item": "r2-ty-0", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, - { - "exception": "clean", - "item": "ref-00009", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00010", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, { "exception": "r2_year_transposed", - "item": "r2-ty-0", + "item": "r2-ty-3", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -530,17 +526,6 @@ "right": "new chart", "screen": "proceed" }, - { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-8", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, { "exception": "concurrent_same", "item": "ref-00012", @@ -560,8 +545,8 @@ "screen": "proceed" }, { - "exception": "heldout_first_typo", - "item": "held-f-6", + "exception": "heldout_last_typo", + "item": "held-l-8", "record": "close", "record_why": [ "same date of birth and first name, another last name", @@ -587,20 +572,11 @@ "screen": "proceed" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-11", - "record": "close", - "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "heldout_first_typo", - "item": "held-f-11", + "exception": "pl_initial", + "item": "pl-in-5", "record": "close", "record_why": [ + "same date of birth and first name, another last name", "same date of birth, a similar name" ], "right": "no chart: duplicate", @@ -615,22 +591,31 @@ "screen": "proceed" }, { - "exception": "pl_married_moved", - "item": "pl-mm-2", + "exception": "r2_name_swap", + "item": "r2-sw-2", "record": "close", "record_why": [ - "same date of birth and first name, another last name" + "first and last name swapped" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_married", - "item": "plant-m-6", + "exception": "pl_ambiguous_same_person", + "item": "pl-as-10", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" + "same or similar name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "pl_initial", + "item": "pl-in-10", + "record": "close", + "record_why": [ + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -644,26 +629,26 @@ "screen": "proceed" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-11", + "exception": "clean", + "item": "ref-00018", "record": "none", "record_why": [], - "right": "no chart: duplicate", + "right": "new chart", "screen": "proceed" }, { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-10", + "exception": "rv_last_typo_dob_typo", + "item": "rv-lt-5", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { - "exception": "planted_hyphenated", - "item": "plant-h-9", + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-1", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -672,26 +657,33 @@ "screen": "stop" }, { - "exception": "clean", - "item": "ref-00018", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" + "exception": "heldout_last_typo", + "item": "held-l-11", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-1", - "record": "none", - "record_why": [], + "exception": "heldout_married_dob_typo", + "item": "held-md-10", + "record": "close", + "record_why": [ + "same first name, another last name, same phone, a mistyped date of birth" + ], "right": "no chart: duplicate", "screen": "proceed" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-11", - "record": "none", - "record_why": [], + "exception": "pl_married_moved", + "item": "pl-mm-8", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" + ], "right": "no chart: duplicate", "screen": "stop" }, @@ -713,6 +705,16 @@ "right": "no chart: duplicate", "screen": "stop" }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-11", + "record": "close", + "record_why": [ + "same first name, another last name, same phone, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, { "exception": "clean", "item": "ref-00020", @@ -722,52 +724,59 @@ "screen": "proceed" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-3", + "exception": "planted_swapped_dob", + "item": "plant-s-3", "record": "close", "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" + "same name, day and month swapped" ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { - "exception": "r2_name_swap", - "item": "r2-sw-9", + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-2", "record": "close", "record_why": [ - "first and last name swapped" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_married", - "item": "plant-m-5", - "record": "close", + "exception": "dup_resend", + "item": "ref-00021", + "record": "exact", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" + "same first name, last name and date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, { "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-2", + "item": "lim-nn-8", "record": "none", "record_why": [], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "dup_resend", - "item": "ref-00021", - "record": "exact", + "exception": "rv_last_typo_dob_typo", + "item": "rv-lt-4", + "record": "close", "record_why": [ - "same first name, last name and date of birth" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-11", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", "screen": "stop" }, { @@ -779,42 +788,32 @@ "screen": "proceed" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-10", - "record": "close", - "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-1", + "exception": "heldout_last_typo", + "item": "held-l-1", "record": "close", "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" + "same date of birth, a similar name" ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { - "exception": "planted_hyphenated", - "item": "plant-h-10", + "exception": "pl_ambiguous_same_person", + "item": "pl-as-0", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-0", + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-9", "record": "none", "record_why": [], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "clean", @@ -825,21 +824,49 @@ "screen": "proceed" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-4", + "exception": "pl_married_moved", + "item": "pl-mm-9", "record": "close", "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { - "exception": "ambiguous_same_person", - "item": "ref-00024", + "exception": "r2_name_swap", + "item": "r2-sw-3", + "record": "close", + "record_why": [ + "first and last name swapped" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "rv_last_typo_dob_typo", + "item": "rv-lt-3", + "record": "close", + "record_why": [ + "same or similar name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-3", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "ambiguous_same_person", + "item": "ref-00024", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -861,8 +888,19 @@ "screen": "proceed" }, { - "exception": "heldout_first_typo", - "item": "held-f-9", + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-8", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-11", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -897,18 +935,18 @@ "screen": "stop" }, { - "exception": "r2_name_swap", - "item": "r2-sw-7", + "exception": "pl_ambiguous_same_person", + "item": "pl-as-6", "record": "close", "record_why": [ - "first and last name swapped" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-8", + "exception": "pl_initial", + "item": "pl-in-7", "record": "close", "record_why": [ "same date of birth and first name, another last name", @@ -934,12 +972,12 @@ "screen": "proceed" }, { - "exception": "planted_hyphenated", - "item": "plant-h-5", + "exception": "pl_married_moved", + "item": "pl-mm-4", "record": "close", "record_why": [ "same date of birth and first name, another last name", - "same date of birth, a similar name" + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" @@ -961,44 +999,55 @@ "screen": "stop" }, { - "exception": "pl_married_moved", - "item": "pl-mm-10", + "exception": "planted_swapped_dob", + "item": "plant-s-10", "record": "close", "record_why": [ - "same date of birth and first name, another last name" + "same name, day and month swapped" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-4", - "record": "none", - "record_why": [], + "exception": "r2_year_transposed", + "item": "r2-ty-4", + "record": "close", + "record_why": [ + "same or similar name, a mistyped date of birth" + ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_married", - "item": "plant-m-3", + "exception": "pl_ambiguous_same_person", + "item": "pl-as-2", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-7", + "exception": "pl_married_moved", + "item": "pl-mm-11", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-7", + "record": "close", + "record_why": [ + "same first name, another last name, same phone, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "proceed" + }, { "exception": "dup_existing", "item": "ref-00034", @@ -1018,8 +1067,8 @@ "screen": "proceed" }, { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-10", + "exception": "heldout_last_typo", + "item": "held-l-10", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -1028,11 +1077,11 @@ "screen": "stop" }, { - "exception": "r2_name_swap", - "item": "r2-sw-2", + "exception": "heldout_last_typo", + "item": "held-l-2", "record": "close", "record_why": [ - "first and last name swapped" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -1054,8 +1103,8 @@ "screen": "proceed" }, { - "exception": "pl_initial", - "item": "pl-in-5", + "exception": "heldout_first_typo", + "item": "held-f-5", "record": "close", "record_why": [ "same date of birth and first name, another last name", @@ -1065,8 +1114,8 @@ "screen": "stop" }, { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-6", + "exception": "heldout_first_typo", + "item": "held-f-6", "record": "close", "record_why": [ "same date of birth and first name, another last name", @@ -1076,18 +1125,16 @@ "screen": "stop" }, { - "exception": "r2_name_swap", - "item": "r2-sw-8", - "record": "close", - "record_why": [ - "first and last name swapped" - ], + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-7", + "record": "none", + "record_why": [], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "pl_initial", - "item": "pl-in-8", + "exception": "heldout_first_typo", + "item": "held-f-8", "record": "close", "record_why": [ "same date of birth and first name, another last name", @@ -1113,29 +1160,31 @@ "screen": "proceed" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-9", - "record": "none", - "record_why": [], + "exception": "r2_year_transposed", + "item": "r2-ty-9", + "record": "close", + "record_why": [ + "same or similar name, a mistyped date of birth" + ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-10", + "exception": "planted_hyphenated", + "item": "plant-h-10", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "pl_married_moved", - "item": "pl-mm-9", + "exception": "planted_hyphenated", + "item": "plant-h-9", "record": "close", "record_why": [ - "same date of birth and first name, another last name" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -1149,22 +1198,21 @@ "screen": "stop" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-9", + "exception": "planted_married", + "item": "plant-m-9", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-5", + "exception": "pl_ambiguous_same_person", + "item": "pl-as-4", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -1178,11 +1226,11 @@ "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-10", + "exception": "pl_ambiguous_same_person", + "item": "pl-as-9", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -1198,79 +1246,93 @@ "screen": "stop" }, { - "exception": "pl_married_moved", - "item": "pl-mm-11", + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-10", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_last_typo", + "item": "held-l-6", "record": "close", "record_why": [ - "same date of birth and first name, another last name" + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-6", + "exception": "rv_last_typo_dob_typo", + "item": "rv-lt-0", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { - "exception": "pl_married_moved", - "item": "pl-mm-0", + "exception": "heldout_married_dob_typo", + "item": "held-md-6", "record": "close", "record_why": [ - "same date of birth and first name, another last name" + "same first name, another last name, same phone, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-6", - "record": "none", - "record_why": [], + "exception": "r2_year_transposed", + "item": "r2-ty-1", + "record": "close", + "record_why": [ + "same or similar name, a mistyped date of birth" + ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { - "exception": "pl_married_moved", - "item": "pl-mm-1", + "exception": "pl_ambiguous_same_person", + "item": "pl-as-7", "record": "close", "record_why": [ - "same date of birth and first name, another last name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-8", + "exception": "pl_initial", + "item": "pl-in-11", "record": "close", "record_why": [ - "same date of birth and first name, another last name", "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-4", - "record": "none", - "record_why": [], + "exception": "r2_name_swap", + "item": "r2-sw-4", + "record": "close", + "record_why": [ + "first and last name swapped" + ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-9", - "record": "none", - "record_why": [], + "exception": "r2_name_swap", + "item": "r2-sw-9", + "record": "close", + "record_why": [ + "first and last name swapped" + ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { "exception": "clean", @@ -1281,12 +1343,12 @@ "screen": "proceed" }, { - "exception": "planted_hyphenated", - "item": "plant-h-6", + "exception": "pl_married_moved", + "item": "pl-mm-5", "record": "close", "record_why": [ "same date of birth and first name, another last name", - "same date of birth, a similar name" + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" @@ -1300,67 +1362,56 @@ "screen": "proceed" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-3", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "r2_name_swap", - "item": "r2-sw-3", + "exception": "heldout_first_typo", + "item": "held-f-3", "record": "close", "record_why": [ - "first and last name swapped" + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "pl_married_moved", - "item": "pl-mm-4", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" - ], + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-11", + "record": "none", + "record_why": [], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-5", - "record": "close", - "record_why": [ - "same name, a mistyped date of birth" - ], + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-2", + "record": "none", + "record_why": [], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-3", + "exception": "planted_swapped_dob", + "item": "plant-s-4", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same name, day and month swapped" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_swapped_dob", - "item": "plant-s-3", + "exception": "planted_married", + "item": "plant-m-5", "record": "close", "record_why": [ - "same name, day and month swapped" + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-1", + "exception": "planted_hyphenated", + "item": "plant-h-1", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -1379,8 +1430,8 @@ "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-3", + "exception": "planted_hyphenated", + "item": "plant-h-3", "record": "close", "record_why": [ "same date of birth and first name, another last name", @@ -1401,16 +1452,44 @@ "screen": "stop" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-3", + "exception": "planted_married", + "item": "plant-m-1", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "session_expiry", + "item": "ref-00046", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "clean", + "item": "ref-00047", "record": "none", "record_why": [], + "right": "new chart", + "screen": "proceed" + }, + { + "exception": "rv_last_typo_dob_typo", + "item": "rv-lt-2", + "record": "close", + "record_why": [ + "same or similar name, a mistyped date of birth" + ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { - "exception": "planted_hyphenated", - "item": "plant-h-1", + "exception": "heldout_first_typo", + "item": "held-f-9", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -1419,37 +1498,37 @@ "screen": "stop" }, { - "exception": "pl_initial", - "item": "pl-in-9", + "exception": "pl_ambiguous_same_person", + "item": "pl-as-11", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "session_expiry", - "item": "ref-00046", + "exception": "clean", + "item": "ref-00048", "record": "none", "record_why": [], "right": "new chart", "screen": "proceed" }, { - "exception": "pl_married_moved", - "item": "pl-mm-8", + "exception": "planted_hyphenated", + "item": "plant-h-8", "record": "close", "record_why": [ "same date of birth and first name, another last name", - "same date of birth and first name, another last name" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-11", + "exception": "planted_hyphenated", + "item": "plant-h-11", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -1458,11 +1537,21 @@ "screen": "stop" }, { - "exception": "planted_hyphenated", - "item": "plant-h-8", + "exception": "pl_married_moved", + "item": "pl-mm-7", "record": "close", "record_why": [ "same date of birth and first name, another last name", + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_first_typo", + "item": "held-f-0", + "record": "close", + "record_why": [ "same date of birth, a similar name" ], "right": "no chart: duplicate", @@ -1470,63 +1559,82 @@ }, { "exception": "clean", - "item": "ref-00047", + "item": "ref-00049", "record": "none", "record_why": [], "right": "new chart", "screen": "proceed" }, { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-2", + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-9", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-4", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same first name, another last name, same phone, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { - "exception": "heldout_last_typo", - "item": "held-l-10", + "exception": "r2_year_transposed", + "item": "r2-ty-8", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-4", + "exception": "clean", + "item": "ref-00050", + "record": "none", + "record_why": [], + "right": "new chart", + "screen": "stop" + }, + { + "exception": "rv_last_typo_dob_typo", + "item": "rv-lt-9", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-8", - "record": "none", - "record_why": [], + "exception": "pl_ambiguous_same_person", + "item": "pl-as-5", + "record": "close", + "record_why": [ + "same or similar name, a mistyped date of birth" + ], "right": "no chart: duplicate", "screen": "stop" }, { "exception": "clean", - "item": "ref-00048", + "item": "ref-00051", "record": "none", "record_why": [], "right": "new chart", "screen": "proceed" }, { - "exception": "planted_married", - "item": "plant-m-10", - "record": "close", + "exception": "dup_existing", + "item": "ref-00052", + "record": "exact", "record_why": [ + "same first name, last name and date of birth", "same date of birth and first name, another last name" ], "right": "no chart: duplicate", @@ -1534,7 +1642,7 @@ }, { "exception": "heldout_married_dob_typo", - "item": "held-md-6", + "item": "held-md-3", "record": "close", "record_why": [ "same first name, another last name, same phone, a mistyped date of birth" @@ -1542,9 +1650,48 @@ "right": "no chart: duplicate", "screen": "proceed" }, + { + "exception": "pl_married_moved", + "item": "pl-mm-6", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "r2_year_transposed", + "item": "r2-ty-5", + "record": "close", + "record_why": [ + "same or similar name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-6", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "planted_hyphenated", + "item": "plant-h-2", + "record": "close", + "record_why": [ + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "heldout_first_typo", - "item": "held-f-0", + "item": "held-f-2", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -1553,83 +1700,81 @@ "screen": "stop" }, { - "exception": "clean", - "item": "ref-00049", + "exception": "missing_optional", + "item": "ref-00053", "record": "none", "record_why": [], "right": "new chart", "screen": "proceed" }, { - "exception": "clean", - "item": "ref-00050", + "exception": "common_surname", + "item": "ref-00054", "record": "none", "record_why": [], "right": "new chart", "screen": "stop" }, { - "exception": "heldout_first_typo", - "item": "held-f-7", + "exception": "pl_married_moved", + "item": "pl-mm-3", "record": "close", "record_why": [ "same date of birth and first name, another last name", - "same date of birth, a similar name" + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" }, { "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-5", + "item": "lim-nn-1", "record": "none", "record_why": [], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-7", + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-10", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "clean", - "item": "ref-00051", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "dup_existing", - "item": "ref-00052", - "record": "exact", + "exception": "pl_married_moved", + "item": "pl-mm-1", + "record": "close", "record_why": [ - "same first name, last name and date of birth", "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "pl_married_moved", - "item": "pl-mm-3", + "exception": "r2_name_swap", + "item": "r2-sw-1", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" + "first and last name swapped" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-2", + "exception": "missing_required", + "item": "ref-00055", + "record": "unreadable", + "record_why": [], + "right": "no chart", + "screen": "stop" + }, + { + "exception": "r2_nickname", + "item": "r2-nk-0", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -1637,9 +1782,19 @@ "right": "no chart: duplicate", "screen": "stop" }, + { + "exception": "r2_year_transposed", + "item": "r2-ty-6", + "record": "close", + "record_why": [ + "same or similar name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, { "exception": "planted_hyphenated", - "item": "plant-h-4", + "item": "plant-h-5", "record": "close", "record_why": [ "same date of birth and first name, another last name", @@ -1649,104 +1804,98 @@ "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-2", + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-0", "record": "close", "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" + "same date of birth, a similar name" ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-11", + "exception": "rv_nickname_dob_typo", + "item": "rv-nd-0", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "missing_optional", - "item": "ref-00053", + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-1", "record": "none", "record_why": [], - "right": "new chart", + "right": "no chart: duplicate", "screen": "proceed" }, { - "exception": "common_surname", - "item": "ref-00054", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "planted_hyphenated", - "item": "plant-h-2", + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-5", "record": "close", "record_why": [ + "same date of birth and first name, another last name", "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-1", + "exception": "pl_ambiguous_same_person", + "item": "pl-as-1", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_married", - "item": "plant-m-7", + "exception": "planted_hyphenated", + "item": "plant-h-4", "record": "close", "record_why": [ "same date of birth and first name, another last name", - "same date of birth and first name, another last name" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "pl_married_moved", - "item": "pl-mm-5", + "exception": "r2_nickname", + "item": "r2-nk-1", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-1", + "exception": "heldout_first_typo", + "item": "held-f-10", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "missing_required", - "item": "ref-00055", - "record": "unreadable", - "record_why": [], - "right": "no chart", + "exception": "rv_nickname_dob_typo", + "item": "rv-nd-1", + "record": "close", + "record_why": [ + "same or similar name, a mistyped date of birth" + ], + "right": "no chart: duplicate", "screen": "stop" }, { "exception": "r2_nickname", - "item": "r2-nk-0", + "item": "r2-nk-2", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -1755,49 +1904,60 @@ "screen": "stop" }, { - "exception": "heldout_last_typo", - "item": "held-l-6", + "exception": "rv_nickname_dob_typo", + "item": "rv-nd-2", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-0", + "exception": "r2_year_transposed", + "item": "r2-ty-7", + "record": "close", + "record_why": [ + "same or similar name, a mistyped date of birth" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "heldout_married_dob_typo", + "item": "held-md-8", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same first name, another last name, same phone, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-4", + "exception": "heldout_last_typo", + "item": "held-l-3", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "r2_nickname", - "item": "r2-nk-1", + "exception": "heldout_last_typo", + "item": "held-l-4", "record": "close", "record_why": [ + "same date of birth and first name, another last name", "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "pl_initial", - "item": "pl-in-10", + "exception": "r2_nickname", + "item": "r2-nk-3", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -1807,7 +1967,7 @@ }, { "exception": "planted_married", - "item": "plant-m-2", + "item": "plant-m-10", "record": "close", "record_why": [ "same date of birth and first name, another last name" @@ -1816,54 +1976,57 @@ "screen": "stop" }, { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-1", - "record": "none", - "record_why": [], + "exception": "rv_nickname_dob_typo", + "item": "rv-nd-3", + "record": "close", + "record_why": [ + "same or similar name, a mistyped date of birth" + ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "r2_nickname", - "item": "r2-nk-2", + "exception": "r2_name_swap", + "item": "r2-sw-5", "record": "close", "record_why": [ - "same date of birth, a similar name" + "first and last name swapped" ], "right": "no chart: duplicate", "screen": "stop" }, { "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-7", + "item": "lim-nn-4", "record": "none", "record_why": [], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-8", + "exception": "r2_name_swap", + "item": "r2-sw-0", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "first and last name swapped" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "r2_nickname", - "item": "r2-nk-3", + "exception": "planted_married", + "item": "plant-m-6", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-4", + "exception": "heldout_first_typo", + "item": "held-f-4", "record": "close", "record_why": [ "same date of birth and first name, another last name", @@ -1873,92 +2036,82 @@ "screen": "stop" }, { - "exception": "r2_nickname", - "item": "r2-nk-4", + "exception": "rv_last_typo_dob_typo", + "item": "rv-lt-6", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", - "screen": "stop" + "screen": "proceed" }, { - "exception": "r2_name_swap", - "item": "r2-sw-10", + "exception": "r2_nickname", + "item": "r2-nk-4", "record": "close", "record_why": [ - "first and last name swapped" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "r2_nickname", - "item": "r2-nk-5", + "exception": "rv_nickname_dob_typo", + "item": "rv-nd-4", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-5", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "r2_name_swap", - "item": "r2-sw-5", + "exception": "planted_hyphenated", + "item": "plant-h-6", "record": "close", "record_why": [ - "first and last name swapped" + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "r2_year_transposed", - "item": "r2-ty-3", + "exception": "r2_nickname", + "item": "r2-nk-5", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { "exception": "pl_married_moved", - "item": "pl-mm-6", + "item": "pl-mm-0", "record": "close", "record_why": [ - "same date of birth and first name, another last name", "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "pl_initial", - "item": "pl-in-4", + "exception": "r2_name_swap", + "item": "r2-sw-6", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" + "first and last name swapped" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "planted_hyphenated", - "item": "plant-h-7", + "exception": "rv_nickname_dob_typo", + "item": "rv-nd-5", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "stop" @@ -1974,32 +2127,47 @@ "screen": "stop" }, { - "exception": "pl_second_surname", - "item": "pl-ss-1", + "exception": "r2_name_swap", + "item": "r2-sw-7", "record": "close", "record_why": [ - "same date of birth, a similar name" + "first and last name swapped" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "pl_initial", - "item": "pl-in-6", + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-4", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "planted_married", + "item": "plant-m-11", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "r2_name_swap", - "item": "r2-sw-0", + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-7", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "proceed" + }, + { + "exception": "pl_second_surname", + "item": "pl-ss-1", "record": "close", "record_why": [ - "first and last name swapped" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -2014,14 +2182,6 @@ "right": "no chart: duplicate", "screen": "stop" }, - { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-6", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "stop" - }, { "exception": "pl_second_surname", "item": "pl-ss-3", @@ -2043,26 +2203,24 @@ "screen": "stop" }, { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-7", + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-3", "record": "none", "record_why": [], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-5", - "record": "close", - "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" - ], + "exception": "limit_married_moved_dob_typo", + "item": "lim-mm-8", + "record": "none", + "record_why": [], "right": "no chart: duplicate", "screen": "proceed" }, { - "exception": "pl_initial", - "item": "pl-in-11", + "exception": "pl_second_surname", + "item": "pl-ss-5", "record": "close", "record_why": [ "same date of birth, a similar name" @@ -2071,31 +2229,32 @@ "screen": "stop" }, { - "exception": "heldout_married_dob_typo", - "item": "held-md-8", + "exception": "rv_last_typo_dob_typo", + "item": "rv-lt-7", "record": "close", "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" + "same or similar name, a mistyped date of birth" ], "right": "no chart: duplicate", "screen": "proceed" }, { - "exception": "pl_initial", - "item": "pl-in-0", + "exception": "pl_married_moved", + "item": "pl-mm-2", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "pl_second_surname", - "item": "pl-ss-5", + "exception": "planted_married", + "item": "plant-m-7", "record": "close", "record_why": [ - "same date of birth, a similar name" + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" ], "right": "no chart: duplicate", "screen": "stop" @@ -2105,7 +2264,7 @@ "item": "r2-pn-0", "record": "close", "record_why": [ - "same name, a mistyped date of birth (a prior name on the chart)" + "same or similar name, a mistyped date of birth (a prior name on the chart)" ], "right": "no chart: duplicate", "screen": "proceed" @@ -2115,69 +2274,59 @@ "item": "r2-pn-1", "record": "close", "record_why": [ - "same name, a mistyped date of birth (a prior name on the chart)" + "same or similar name, a mistyped date of birth (a prior name on the chart)" ], "right": "no chart: duplicate", "screen": "proceed" }, { - "exception": "r2_name_swap", - "item": "r2-sw-4", - "record": "close", - "record_why": [ - "first and last name swapped" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-9", + "exception": "r2_prior_name_moved_dob_typo", + "item": "r2-pn-2", "record": "close", "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" + "same or similar name, a mistyped date of birth (a prior name on the chart)" ], "right": "no chart: duplicate", "screen": "proceed" }, { "exception": "r2_prior_name_moved_dob_typo", - "item": "r2-pn-2", + "item": "r2-pn-3", "record": "close", "record_why": [ - "same name, a mistyped date of birth (a prior name on the chart)" + "same or similar name, a mistyped date of birth (a prior name on the chart)" ], "right": "no chart: duplicate", "screen": "proceed" }, { - "exception": "planted_married", - "item": "plant-m-8", + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-9", "record": "close", "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" }, { - "exception": "r2_prior_name_moved_dob_typo", - "item": "r2-pn-3", + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-7", "record": "close", "record_why": [ - "same name, a mistyped date of birth (a prior name on the chart)" + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", - "screen": "proceed" + "screen": "stop" }, { - "exception": "pl_married_moved", - "item": "pl-mm-7", + "exception": "pl_initial", + "item": "pl-in-8", "record": "close", "record_why": [ "same date of birth and first name, another last name", - "same date of birth and first name, another last name" + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" @@ -2187,7 +2336,7 @@ "item": "r2-pn-4", "record": "close", "record_why": [ - "same name, a mistyped date of birth (a prior name on the chart)" + "same or similar name, a mistyped date of birth (a prior name on the chart)" ], "right": "no chart: duplicate", "screen": "proceed" @@ -2197,29 +2346,60 @@ "item": "r2-pn-5", "record": "close", "record_why": [ - "same name, a mistyped date of birth (a prior name on the chart)" + "same or similar name, a mistyped date of birth (a prior name on the chart)" ], "right": "no chart: duplicate", "screen": "proceed" }, { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-2", + "exception": "heldout_last_typo", + "item": "held-l-7", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth, a similar name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "planted_married", + "item": "plant-m-4", + "record": "close", + "record_why": [ + "same date of birth and first name, another last name", + "same date of birth and first name, another last name" + ], + "right": "no chart: duplicate", + "screen": "stop" + }, + { + "exception": "pl_both_typos_same_contact", + "item": "pl-bt-4", "record": "close", "record_why": [ - "same name, a mistyped date of birth" + "same date of birth and first name, another last name", + "same date of birth, a similar name" ], "right": "no chart: duplicate", "screen": "stop" + }, + { + "exception": "limit_new_name_moved_same_dob", + "item": "lim-nn-6", + "record": "none", + "record_why": [], + "right": "no chart: duplicate", + "screen": "stop" } ], "summary": { - "items": 231, + "items": 249, "new_chart_items": 45, "record": { - "caught_duplicates": 160, + "caught_duplicates": 178, "decisions": { - "close": 153, + "close": 171, "exact": 7, "none": 69, "unreadable": 2 @@ -2234,7 +2414,7 @@ "missed_duplicates": 24 }, "screen": { - "caught_duplicates": 154, + "caught_duplicates": 160, "false_stops": 10, "false_stops_by_exception": { "clean": 2, @@ -2242,14 +2422,15 @@ "shared_dob": 3, "slow_save": 1 }, - "false_stops_per_100_items": 4.3, + "false_stops_per_100_items": 4.0, "missed_by_exception": { "heldout_married_dob_typo": 12, "limit_married_moved_dob_typo": 12, - "r2_prior_name_moved_dob_typo": 6 + "r2_prior_name_moved_dob_typo": 6, + "rv_last_typo_dob_typo": 12 }, - "missed_duplicates": 30 + "missed_duplicates": 42 }, - "true_duplicates": 184 + "true_duplicates": 202 } } diff --git a/packages/flow/docs/DUPLICATE_CHECK.md b/packages/flow/docs/DUPLICATE_CHECK.md index 37e52f1ac..6186804ad 100644 --- a/packages/flow/docs/DUPLICATE_CHECK.md +++ b/packages/flow/docs/DUPLICATE_CHECK.md @@ -64,53 +64,91 @@ on the OpenEMR referral program, and the soak admits that version afresh ## Asking the system of record instead -When the run's effect verifier reads a FHIR server or OpenEMR's patient API, -`no_duplicate_candidate` searches the system of record for the patient instead -of reading the on-screen list (`runtime/checks/duplicate_record.py`, a check -whose source is the record). With no such channel, the on-screen list decides, -as above. +`no_duplicate_candidate` can search the system of record for the patient +instead of reading the on-screen list (`runtime/checks/duplicate_record.py`, a +check whose source is the record). The channels: -| Source | Scope | -|---|---| -| `FhirPatientSource` | generic FHIR R4 (`Patient` search with standard parameters); any R4 server | -| `RestPatientSource` | OpenEMR adapter: OpenEMR's standard REST `/api/patient` | -| `SqlPatientSource` | OpenEMR adapter, fallback: a read-only query on `patient_data`, wired by hand, never by default | - -**One source for every path.** `Replayer` resolves the source itself, from the -effect verifier it is given (`resolve_duplicate_source`). So qualification -(`qualify`), a run (`build_replayer`), a resume and the lifecycle proof that -read the same system of record use the same check, and none of them can pick -another. `tests/test_duplicate_source_paths.py` scans every `Replayer(...)` +| Source | Scope | Used | +|---|---|---| +| `FhirPatientSource` | generic FHIR R4: `Patient` search with standard parameters, for any R4 server | whenever the run's effect verifier reads FHIR | +| `RestPatientSource` | OpenEMR adapter: OpenEMR's standard REST `/api/patient` | only for a program pinned to the record check (below) | +| `SqlPatientSource` | OpenEMR adapter, fallback: a read-only query on `patient_data` (MySQL or MariaDB `%s` parameters; `paramstyle="qmark"` for SQLite) | only when wired by hand | + +**One source for every path.** `Replayer` resolves the source itself, from +its own effect verifier and the program's pin (`resolve_duplicate_source`). +So qualification, a run, a resume and the lifecycle proof that read the same +system of record use the same check: +- no construction path can choose another; +- a qualification hook can't set one; +- a verifier the resolution doesn't recognize gives no source rather than an + error. + +`tests/test_duplicate_source_paths.py` scans every `Replayer(...)` construction to keep it so. -**The program can pin it.** The postcondition's `candidate_source: record` -makes the record check part of the qualified program: a run with no record -channel fails the step, and never reads the screen instead. -`candidate_source: screen` keeps the list even when a channel exists. Unset, -the run's channel decides. The deployment's `runtime.duplicate_check: screen` -turns the record check off for unpinned programs; a program pinned to -`record` then fails closed. - -The check searches by the date of birth, by the date of birth with day and -month swapped, by each part of the last and first name, by first name, and by -MRN, phone and email when the run has them. +**The program decides, and qualification sees it.** The postcondition's +`candidate_source` is part of the qualified program: +- `record`: the record check, with the FHIR or OpenEMR REST channel. A run + with no record channel fails the step and never reads the screen instead. +- `screen`: the on-screen list, even when a channel exists. +- unset: the FHIR channel when the run has one, else the screen. + +**Parameter names.** The check reads `fname`, `lname`, `DOB`, and when +present `mrn`, `phone_home`, `email` and `street`: OpenEMR's referral names. +A program for another app maps its own with `candidate_record_fields`, for +example `{"fname": "given", "lname": "family", "DOB": "birth_date"}`. + +The check searches: +- by the date of birth, and by the date with day and month swapped; +- by each part of the last and first name, and by the first name; +- by MRN, phone and email when the run has them. + It reads every page, and it decides from whole records: | Decision | When | What happens | |---|---|---| | `exact` | same first name, last name and date of birth, or the same MRN | stop: the chart exists | -| `close` | same name (or a nickname, an initial, or the chart's prior name) with day and month swapped, or with the date of birth one field off and either the exact name or a shared phone, email or street; same date of birth with a similar name or a shared hyphenated part; first and last name swapped; same date of birth and first name under another last name; another name with the same date of birth and a shared contact detail, or a near date of birth and two | stop for a person; the halt names each record and why | +| `close` | see the list below | stop for a person; the halt names each record and why | | `none` | no record is this patient | proceed | -| `unreadable` | unreachable, refused credentials, not a result set, a reported total that doesn't match what was read, more than 50 pages, a full REST page with no next link, a missing or non-ISO date of birth | stop (fail closed) | +| `unreadable` | see the list below | stop (fail closed) | + +A record is `close` when it has: +- the same or a similar name, a nickname, an initial or the chart's prior + name, with day and month swapped, or with the date of birth one field off + and at most one name part differing (or a shared phone, email or street); +- the same date of birth with a similar name or a shared surname part; +- first and last name swapped; +- the same date of birth and first name under another last name; +- another name with the same date of birth and a shared contact detail, or a + near date of birth and two. + +A record date that is only a year or a year and month counts as no date of +birth, so a name match is `close`, never a non-match. + +The check is `unreadable` when: +- the channel is unreachable, or refuses the credentials; +- the reply is not a result set, or a REST body carries any error field or + no `data` list; +- a reported total doesn't match what was read; +- the search runs past 50 pages, or a REST page is full with no next link; +- a next-page link points to another host; +- a record has no id; +- the run's date of birth is missing or not ISO. Sharing only a last name, or only a date of birth, doesn't stop the item. -Measured offline on the production soak's seed-7 stream, with true -duplicates planted: `benchmark/dup_check/README.md`. +**The stop reason.** A record-check stop reads `failed: no_duplicate_candidate +(record check via : , ...)`. The channel is +`none` when a pinned program had no channel. Decision `unreadable` is a +problem with the program or its channel. `exact` and `close` are about the +item. Not catchable from the referral alone: a person whose name changed, who moved, and whose date of birth is mistyped or merely shared. Nothing the referral carries ties them to the chart; that needs an MRN, an insurance id, or a person. + +Measured offline on the production soak's seed-7 stream, with true +duplicates planted: `benchmark/dup_check/README.md`. diff --git a/packages/flow/openadapt_flow/deployment.py b/packages/flow/openadapt_flow/deployment.py index 3aaf25f63..3fa342ef1 100644 --- a/packages/flow/openadapt_flow/deployment.py +++ b/packages/flow/openadapt_flow/deployment.py @@ -761,13 +761,6 @@ class RuntimeSection(BaseModel): #: :func:`openadapt_flow.runtime.identity.verify_pixel_identity` and #: docs/LIMITS.md. pixel_verify_enabled: bool = False - #: Where ``no_duplicate_candidate`` gets its evidence. ``auto`` (the - #: default): when ``effects`` reads a FHIR server or OpenEMR's patient API, - #: the check searches the system of record for this patient - #: (``runtime/checks/duplicate_record.py``) and the app's on-screen match - #: list is not read. With no such channel, or ``screen``, the on-screen - #: list decides, as before. - duplicate_check: Literal["auto", "screen"] = "auto" #: Path to an export of the system of record that lists every identifier #: in the namespace (CSV, JSON or one per line). When set, an OCR identity #: check that abstained only on a look-alike identifier (O/0, l/1, B/8) is @@ -1657,9 +1650,6 @@ def build_replayer( state_verifier=(appliance.state_verifier if appliance else None), allow_model_grounding=allow_egress, effect_verifier=effect_verifier, - duplicate_record_source=( - runtime_config.duplicate_check if runtime_config is not None else "auto" - ), api_actuator=api_actuator, durable=durable, use_structural=use_structural, diff --git a/packages/flow/openadapt_flow/ir.py b/packages/flow/openadapt_flow/ir.py index 539e23ee4..01c817ce6 100644 --- a/packages/flow/openadapt_flow/ir.py +++ b/packages/flow/openadapt_flow/ir.py @@ -520,6 +520,10 @@ class Postcondition(BaseModel): #: even when a record channel exists. Unset: the record when the run has #: a record channel, else the screen. candidate_source: Optional[Literal["record", "screen"]] = None + #: NO_DUPLICATE_CANDIDATE, record source: the run parameter that fills + #: each field the record check reads (``fname``, ``lname``, ``DOB``, + #: ``mrn``, ``phone_home``, ``email``, ``street``). Unset: those names. + candidate_record_fields: Optional[dict[str, str]] = None @model_serializer(mode="wrap") def _serialize_compatible(self, handler: Any) -> dict[str, Any]: @@ -536,6 +540,7 @@ def _serialize_compatible(self, handler: Any) -> dict[str, Any]: "candidate_frame_path", "candidate_params", "candidate_source", + "candidate_record_fields", ): if getattr(self, name) is None: data.pop(name, None) diff --git a/packages/flow/openadapt_flow/qualify/campaign.py b/packages/flow/openadapt_flow/qualify/campaign.py index d07913cad..0fa9401e0 100644 --- a/packages/flow/openadapt_flow/qualify/campaign.py +++ b/packages/flow/openadapt_flow/qualify/campaign.py @@ -756,7 +756,12 @@ def _run( kwargs["checkpoint_key"] = crypto.resolve_key(None) if hooks.replayer_kwargs is not None: - kwargs.update(hooks.replayer_kwargs(ctx)) + extra = dict(hooks.replayer_kwargs(ctx)) + if "duplicate_record_source" in extra: + # The duplicate check is resolved from the run's own verifier + # on every path; a hook can't qualify under another one. + raise ValueError("replayer_kwargs may not set duplicate_record_source") + kwargs.update(extra) # The signed lookup settings win over any hook, so a hook can't turn # structure back on during a pass that must not use it. kwargs.update(lookup or {}) diff --git a/packages/flow/openadapt_flow/runtime/checks/duplicate_record.py b/packages/flow/openadapt_flow/runtime/checks/duplicate_record.py index c093e7721..6f3e71d29 100644 --- a/packages/flow/openadapt_flow/runtime/checks/duplicate_record.py +++ b/packages/flow/openadapt_flow/runtime/checks/duplicate_record.py @@ -34,12 +34,25 @@ import unicodedata from dataclasses import dataclass, field from typing import Any, Callable, Iterable, Literal, Mapping, Optional, Protocol -from urllib.parse import urljoin +from urllib.parse import urljoin, urlsplit from openadapt_flow.runtime.checks import CheckSource Decision = Literal["none", "exact", "close", "unreadable"] +#: The fields the check reads, and the run parameter that fills each one by +#: default (OpenEMR's referral parameter names). A program for another app +#: maps its own names with ``candidate_record_fields``. +RECORD_FIELDS: dict[str, str] = { + "fname": "fname", + "lname": "lname", + "DOB": "DOB", + "mrn": "mrn", + "phone_home": "phone_home", + "email": "email", + "street": "street", +} + #: A paging chain longer than this is read as unreadable, not truncated. MAX_PAGES = 50 @@ -381,8 +394,13 @@ def _classify_name( return Candidate(record, "close", "same date of birth, a similar name") if record.dob == swapped(dob): return Candidate(record, "close", "same name, day and month swapped") - if near and (same_first and same_last or tie_breaker(want, record)): - return Candidate(record, "close", "same name, a mistyped date of birth") + # A mistyped date of birth with a name that differs in at most one + # part (a nickname, an initial or a typo in the other), or with a + # shared contact detail. + if near and (same_first or same_last or tie_breaker(want, record)): + return Candidate( + record, "close", "same or similar name, a mistyped date of birth" + ) swapped_names = norm_name(fname) == norm_name(last) and norm_name( lname ) == norm_name(first) @@ -405,6 +423,15 @@ def _classify_name( ISO_DATE = re.compile(r"\d{4}-\d{2}-\d{2}") +def full_date(value: Any) -> str: + """A record's date of birth as full ISO, or "" when it has none or only a + year or a year and month. A partial date is never a non-match: the + record is judged as one with no date of birth, so a name match is close.""" + + text = str(value or "").strip()[:10] + return text if ISO_DATE.fullmatch(text) else "" + + def check_duplicate( source: PatientSource, want: Mapping[str, str] ) -> DuplicateCheckResult: @@ -439,6 +466,8 @@ def check_duplicate( try: for query, value in queries: for record in query(value): + if not record.record_id.strip(): + raise Unreadable("a record came back with no id") seen.setdefault(record.record_id, record) result.searches += 1 except Unreadable as exc: @@ -483,6 +512,16 @@ def _get_json( raise Unreadable("the record channel returned a body that is not JSON") +def _same_origin(base: str, url: str) -> str: + """``url`` if it is on ``base``'s scheme, host and port, else unreadable: + a next-page link to another host must not receive the credentials.""" + + a, b = urlsplit(base), urlsplit(url) + if (a.scheme, a.netloc) != (b.scheme, b.netloc): + raise Unreadable("a next-page link points to another host") + return url + + class FhirPatientSource: """Generic FHIR R4: ``Patient`` search, every page (``Bundle.link[next]``). @@ -568,7 +607,7 @@ def search(self, params: dict[str, str]) -> list[PatientRecord]: raw = link.get("url") if not isinstance(raw, str) or not raw.strip(): raise Unreadable("a next-page link has no URL") - url = urljoin(at, raw.strip()) + url = _same_origin(self.base_url, urljoin(at, raw.strip())) if total is not None and total != len(out): raise Unreadable( f"the search reported {total} record(s) and {len(out)} were read" @@ -624,7 +663,7 @@ def pair(n: dict[str, Any]) -> tuple[str, str]: record_id=str(resource.get("id") or ""), fname=first, lname=last, - dob=str(resource.get("birthDate") or ""), + dob=full_date(resource.get("birthDate")), mrn=mrn, phone=phone, street=str(lines[0]) if lines else "", @@ -658,18 +697,26 @@ class SqlPatientSource: OpenEMR's. For a deployment with no FHIR or REST read. ``connect`` returns a DB-API - connection with a SELECT-only account. Uses ``qmark`` parameters. + connection with a SELECT-only account. OpenEMR runs on MySQL or MariaDB, + so parameters default to ``%s`` (``paramstyle="format"``); pass + ``paramstyle="qmark"`` for SQLite. The SQL itself is portable (the + wildcard is bound, not concatenated). """ channel = "sql" def __init__( - self, connect: Callable[[], Any], *, table: str = "patient_data" + self, + connect: Callable[[], Any], + *, + table: str = "patient_data", + paramstyle: Literal["format", "qmark"] = "format", ) -> None: if not re.fullmatch(r"[A-Za-z_][A-Za-z0-9_.]{0,63}", table): raise ValueError("table must be a plain identifier") self._connect = connect self.table = table + self.mark = "%s" if paramstyle == "format" else "?" self.pages_read = 0 def _query(self, where: str, *values: str) -> list[PatientRecord]: @@ -681,7 +728,8 @@ def _query(self, where: str, *values: str) -> list[PatientRecord]: cur = conn.cursor() cur.execute( "SELECT pid, fname, lname, DOB, pubpid, phone_home, street, email, " - f"birth_fname, birth_lname FROM {self.table} WHERE {where}", + f"birth_fname, birth_lname FROM {self.table} " + f"WHERE {where.replace('?', self.mark)}", values, ) rows = cur.fetchall() @@ -701,7 +749,7 @@ def _query(self, where: str, *values: str) -> list[PatientRecord]: str(pid), str(f or ""), str(ln or ""), - str(d or "")[:10], + full_date(d), str(mrn or ""), str(phone or ""), str(street or ""), @@ -716,10 +764,11 @@ def by_birthdate(self, dob: str) -> list[PatientRecord]: def by_family(self, family: str) -> list[PatientRecord]: # FHIR's string search is a case-insensitive prefix; match it. + prefix = family.replace("%", "").replace("_", "") + "%" return self._query( - "LOWER(lname) LIKE LOWER(?) || '%' OR LOWER(birth_lname) LIKE LOWER(?) || '%'", - family, - family, + "LOWER(lname) LIKE LOWER(?) OR LOWER(birth_lname) LIKE LOWER(?)", + prefix, + prefix, ) def by_identifier(self, mrn: str) -> list[PatientRecord]: @@ -833,15 +882,18 @@ def search(self, params: dict[str, str]) -> list[PatientRecord]: ) pages += 1 self.pages_read += 1 - rows = body.get("data") if isinstance(body, dict) else None - if ( - rows is None - and isinstance(body, dict) - and not body.get("validationErrors") - ): - rows = [] # OpenEMR returns no "data" for an empty search + if not isinstance(body, dict): + raise Unreadable("the patient search did not return an object") + errors = [ + k + for k in ("validationErrors", "internalErrors", "errors", "error") + if body.get(k) + ] + if errors: + raise Unreadable("the patient search reported " + ", ".join(errors)) + rows = body.get("data") if not isinstance(rows, list): - raise Unreadable("the patient search did not return a list") + raise Unreadable("the patient search did not return a data list") for row in rows: if not isinstance(row, dict): raise Unreadable("a patient row is not an object") @@ -852,7 +904,7 @@ def search(self, params: dict[str, str]) -> list[PatientRecord]: ), fname=str(row.get("fname") or ""), lname=str(row.get("lname") or ""), - dob=str(row.get("DOB") or "")[:10], + dob=full_date(row.get("DOB")), mrn=str(row.get("pubpid") or ""), phone=str(row.get("phone_home") or ""), street=str(row.get("street") or ""), @@ -874,7 +926,8 @@ def search(self, params: dict[str, str]) -> list[PatientRecord]: links = body.get("links") if isinstance(body, dict) else None nxt = links.get("next") if isinstance(links, dict) else None if isinstance(nxt, str) and nxt.strip(): - url, query = urljoin(at, nxt.strip()), None + url = _same_origin(self.base_url, urljoin(at, nxt.strip())) + query = None elif len(rows) >= self.page_size: raise Unreadable( "a full page with no next link; the search may be truncated" @@ -905,62 +958,95 @@ def by_email(self, email: str) -> list[PatientRecord]: return self.search({"email": email}) -def source_for_verifier(verifier: Any) -> Optional[PatientSource]: - """The patient search on the same channel as a deployment's record check. +def _unwrap(verifier: Any) -> Any: + """The record verifier inside a known wrapper, else ``verifier``.""" - A FHIR verifier gives a FHIR ``Patient`` search at its base URL with its - credentials; an OpenEMR REST verifier on ``.../api/patient`` gives the - REST search. Anything else gives None, and the screen check stays. + seen = 0 + while verifier is not None and seen < 4: + inner = None + for name in ("inner", "_inner", "wrapped", "_wrapped", "verifier", "_verifier"): + candidate = getattr(verifier, name, None) + if candidate is not None and candidate is not verifier: + inner = candidate + break + if inner is None: + return verifier + verifier, seen = inner, seen + 1 + return verifier + + +def source_for_verifier( + verifier: Any, *, allow_rest: bool = False +) -> Optional[PatientSource]: + """The patient search on the same channel as a run's record check. + + A FHIR verifier gives a generic FHIR R4 ``Patient`` search at its base URL + with its credentials. An OpenEMR REST verifier on ``.../api/patient`` + gives the OpenEMR REST adapter, but only with ``allow_rest`` (a program + pinned to the record check). Anything else, or a verifier missing the + attributes these need, gives None. """ from openadapt_flow.runtime.effects import FhirEffectVerifier from openadapt_flow.runtime.effects.rest import RestRecordVerifier - if isinstance(verifier, FhirEffectVerifier): - - def token() -> Optional[str]: - if verifier.token_source is not None: - return verifier.token_source.access_token() - return verifier.access_token - - return FhirPatientSource( - verifier.base_url, - token=token if (verifier.access_token or verifier.token_source) else None, - verify_tls=verifier.verify_tls, - timeout_s=max(verifier.timeout_s, 10.0), - ) - if isinstance(verifier, RestRecordVerifier) and "/api/patient" in ( - verifier.records_path or "" - ): - return RestPatientSource( - verifier.base_url, - path=verifier.records_path, - headers=verifier.headers, - session=verifier._get_session(), - timeout_s=max(verifier.timeout_s, 10.0), - ) + verifier = _unwrap(verifier) + try: + if isinstance(verifier, FhirEffectVerifier): + access_token = getattr(verifier, "access_token", None) + token_source = getattr(verifier, "token_source", None) + + def token() -> Optional[str]: + if token_source is not None: + return token_source.access_token() + return access_token + + return FhirPatientSource( + verifier.base_url, + token=token if (access_token or token_source) else None, + verify_tls=getattr(verifier, "verify_tls", True), + timeout_s=max(float(getattr(verifier, "timeout_s", 10.0)), 10.0), + ) + records_path = str(getattr(verifier, "records_path", "") or "") + if ( + allow_rest + and isinstance(verifier, RestRecordVerifier) + and "/api/patient" in records_path + ): + return RestPatientSource( + verifier.base_url, + path=records_path, + headers=getattr(verifier, "headers", None), + session=verifier._get_session(), + timeout_s=max(float(getattr(verifier, "timeout_s", 10.0)), 10.0), + ) + except Exception: # noqa: BLE001 - an unusable verifier gives no source + return None return None #: ``Replayer(duplicate_record_source=...)``: derive the source from the run's -#: own effect verifier (the default), or keep the on-screen list. +#: own effect verifier (the default). Only tests and embedders pass a source. AUTO = "auto" -SCREEN = "screen" -def resolve_duplicate_source(effect_verifier: Any, requested: Any = AUTO) -> Any: +def resolve_duplicate_source( + effect_verifier: Any, requested: Any = AUTO, *, pinned_record: bool = False +) -> Any: """The one place a run's duplicate-check source is decided. Every ``Replayer`` resolves its source here from the effect verifier it is given, so qualification, a run, a resume and a proof that read the same - system of record use the same check. ``requested`` is ``AUTO`` (derive), - ``SCREEN`` (no record source) or a source object (tests, embedding). + system of record use the same check. With no pin, only a FHIR channel is + used; a program pinned to the record check (``candidate_source: + record``) also accepts OpenEMR's REST channel. ``requested`` is ``AUTO`` + or a source object (tests, embedding). """ - if requested == SCREEN or requested is None: + if requested is None: return None if requested != AUTO: return requested if effect_verifier is None: return None - return source_for_verifier(effect_verifier) + return source_for_verifier(effect_verifier, allow_rest=pinned_record) diff --git a/packages/flow/openadapt_flow/runtime/replayer.py b/packages/flow/openadapt_flow/runtime/replayer.py index ce8c6d321..4f9361111 100644 --- a/packages/flow/openadapt_flow/runtime/replayer.py +++ b/packages/flow/openadapt_flow/runtime/replayer.py @@ -1022,18 +1022,11 @@ def __init__( self.effect_verifier = effect_verifier self.effect_compensator = effect_compensator # The duplicate check's record channel (``runtime/checks/ - # duplicate_record.py``), resolved here from this run's own effect - # verifier, so every construction path (qualification, run, resume, - # proof) that reads the same system of record uses the same check. - # When set, ``no_duplicate_candidate`` asks the system of record - # instead of reading the app's match list. - from openadapt_flow.runtime.checks.duplicate_record import ( - resolve_duplicate_source, - ) - - self.duplicate_record_source = resolve_duplicate_source( - effect_verifier, duplicate_record_source - ) + # duplicate_record.py``). Resolved from this run's own effect verifier + # when the check runs (``_duplicate_source``), so every construction + # path (qualification, run, resume, proof) that reads the same system + # of record uses the same check. Only tests and embedders pass one. + self._duplicate_requested = duplicate_record_source self._last_duplicate_check: Optional[Any] = None # ``run``-only, bundle-bound admission capability. The ordinary # ``replay`` path leaves this unset and preserves its permissive @@ -17747,8 +17740,10 @@ def _no_duplicate_candidate(self, pc: Any, frame_png: bytes) -> bool: or getattr(self, "_run_params", None) or {} ) - source = getattr(self, "duplicate_record_source", None) pinned = getattr(pc, "candidate_source", None) + source = ( + None if pinned == "screen" else self._duplicate_source(pinned == "record") + ) if pinned == "record" and source is None: # The program was qualified with the record check; without a # record channel it must not fall back to the screen. @@ -17758,12 +17753,13 @@ def _no_duplicate_candidate(self, pc: Any, frame_png: bytes) -> bool: self._last_duplicate_check = DuplicateCheckResult( "unreadable", + channel="none", reason="the program requires the record check and this run has " "no record channel", ) return False - if source is not None and pinned != "screen": - return self._no_duplicate_record(source, params) + if source is not None: + return self._no_duplicate_record(source, params, pc) names = tuple(pc.candidate_params or ()) if not pc.candidate_rows or not names or not pc.text: return False @@ -17801,7 +17797,22 @@ def words(value: str) -> list[str]: return False return True - def _no_duplicate_record(self, source: Any, params: dict[str, Any]) -> bool: + def _duplicate_source(self, pinned_record: bool = False) -> Any: + """This run's duplicate-check record source, or None (the screen).""" + + from openadapt_flow.runtime.checks.duplicate_record import ( + resolve_duplicate_source, + ) + + return resolve_duplicate_source( + self.effect_verifier, + getattr(self, "_duplicate_requested", "auto"), + pinned_record=pinned_record, + ) + + def _no_duplicate_record( + self, source: Any, params: dict[str, Any], pc: Any = None + ) -> bool: """The duplicate check with the system of record as its source. Proceeds only when the record channel answers completely and no chart @@ -17810,11 +17821,18 @@ def _no_duplicate_record(self, source: Any, params: dict[str, Any]) -> bool: records found and why (``_describe_postcondition``). """ - from openadapt_flow.runtime.checks.duplicate_record import check_duplicate + from openadapt_flow.runtime.checks.duplicate_record import ( + RECORD_FIELDS, + check_duplicate, + ) + # Which run parameter fills each field: OpenEMR's names by default, + # ``pc.candidate_record_fields`` for any other program. + mapped = getattr(pc, "candidate_record_fields", None) or {} + names = {**RECORD_FIELDS, **dict(mapped)} want = { - name: runtime_param_text(params[name]) if name in params else "" - for name in ("fname", "lname", "DOB", "mrn", "phone_home", "email", "street") + field: runtime_param_text(params[name]) if name in params else "" + for field, name in names.items() } try: result = check_duplicate(source, want) diff --git a/packages/flow/public-artifacts.json b/packages/flow/public-artifacts.json index c8f2dafa6..385296d66 100644 --- a/packages/flow/public-artifacts.json +++ b/packages/flow/public-artifacts.json @@ -133,6 +133,8 @@ "benchmark/dom/finals/perturbation_dom_named_028.png", "benchmark/dom/outcome_matrix.png", "benchmark/dom/results.json", + "benchmark/dup_check/results/seed7-500.json", + "benchmark/dup_check/results/soak-seed7-55.json", "benchmark/effect_e2e/effect_e2e.png", "benchmark/effect_e2e/results.json", "benchmark/effect_readback/results.json", @@ -166,6 +168,29 @@ "benchmark/lending_fault_model/resolution_ladder_results.json", "benchmark/lending_fault_model/results.json", "benchmark/lending_fault_model/swer_results.json", + "benchmark/lifecycle/results/production-2026-10-24/after/arm-ledger.jsonl", + "benchmark/lifecycle/results/production-2026-10-24/after/ledger.jsonl", + "benchmark/lifecycle/results/production-2026-10-24/after/results.json", + "benchmark/lifecycle/results/production-2026-10-24/after/stops.jsonl", + "benchmark/lifecycle/results/production-2026-10-24/after/switch/log-1792873972.jsonl", + "benchmark/lifecycle/results/production-2026-10-24/after/switch/log-1792878330.jsonl", + "benchmark/lifecycle/results/production-2026-10-24/after/switch/switch.json", + "benchmark/lifecycle/results/production-2026-10-24/after/workload.jsonl", + "benchmark/lifecycle/results/production-2026-10-24/before/arm-ledger.jsonl", + "benchmark/lifecycle/results/production-2026-10-24/before/ledger.jsonl", + "benchmark/lifecycle/results/production-2026-10-24/before/results.json", + "benchmark/lifecycle/results/production-2026-10-24/before/stops.jsonl", + "benchmark/lifecycle/results/production-2026-10-24/before/switch/log-1792868032.jsonl", + "benchmark/lifecycle/results/production-2026-10-24/before/switch/switch.json", + "benchmark/lifecycle/results/production-2026-10-24/before/workload.jsonl", + "benchmark/lifecycle/results/production-2026-10-24/recovery-smoke/arm-ledger.jsonl", + "benchmark/lifecycle/results/production-2026-10-24/recovery-smoke/ledger.jsonl", + "benchmark/lifecycle/results/production-2026-10-24/recovery-smoke/results.json", + "benchmark/lifecycle/results/production-2026-10-24/recovery-smoke/stops.jsonl", + "benchmark/lifecycle/results/production-2026-10-24/recovery-smoke/switch/log-1792865787.jsonl", + "benchmark/lifecycle/results/production-2026-10-24/recovery-smoke/switch/log-1792866771.jsonl", + "benchmark/lifecycle/results/production-2026-10-24/recovery-smoke/switch/switch.json", + "benchmark/lifecycle/results/production-2026-10-24/recovery-smoke/workload.jsonl", "benchmark/lifecycle_proof/results/earlier-attempts/proof-12-ledger.jsonl", "benchmark/lifecycle_proof/results/earlier-attempts/proof-13-ledger.jsonl", "benchmark/lifecycle_proof/results/earlier-attempts/proof-14-ledger.jsonl", diff --git a/packages/flow/tests/test_duplicate_record_check.py b/packages/flow/tests/test_duplicate_record_check.py index 2997661fb..17bd07818 100644 --- a/packages/flow/tests/test_duplicate_record_check.py +++ b/packages/flow/tests/test_duplicate_record_check.py @@ -253,7 +253,8 @@ def test_sql_fallback(tmp_path: Path) -> None: ) con.commit() con.close() - result = check_duplicate(SqlPatientSource(lambda: sqlite3.connect(db)), WANT) + source = SqlPatientSource(lambda: sqlite3.connect(db), paramstyle="qmark") + result = check_duplicate(source, WANT) assert result.decision == "close" and result.channel == "sql" assert {c.record.record_id for c in result.candidates} == {"4", "5"} @@ -416,3 +417,96 @@ def test_production_lifecycle_cases_stop( def test_an_initial_does_not_match_another_letter() -> None: want = {"fname": "J.", "lname": "Ashby", "DOB": "1950-02-03"} assert classify(want, rec("Mary", "Ashby", "1950-02-03")) is None + + +# -- review fixes: every read problem is unreadable, never "none" ---------------- + + +@pytest.mark.parametrize( + "body", + [ + {"internalErrors": ["db down"]}, # no data, an error + {"data": [], "validationErrors": {"DOB": "bad"}}, # an error next to data + {}, # no data at all + {"data": {"0": {}}}, # data that isn't a list + ], +) +def test_rest_error_bodies_are_unreadable(serve, body) -> None: + server = serve(lambda p, q: (200, body)) + source = RestPatientSource(server.url, path="/api/patient") + assert check_duplicate(source, WANT).decision == "unreadable" + + +@pytest.mark.parametrize("partial", ["1990", "1990-04", ""]) +def test_a_partial_record_birth_date_is_no_date_not_a_mismatch(serve, partial) -> None: + resource = patient("31", "Rosalind", "Jesswood", partial) + if not partial: + resource.pop("birthDate") + bundle = {"resourceType": "Bundle", "total": 1, "entry": [{"resource": resource}]} + server = serve(lambda p, q: (200, bundle)) + assert check_duplicate(FhirPatientSource(server.url), WANT).decision == "close" + + +def test_a_record_with_no_id_is_unreadable() -> None: + table = TableSource([rec("Ana", "Other", "1990-04-09", rid=" ")]) + assert check_duplicate(table, WANT).decision == "unreadable" + + +def test_a_next_link_to_another_host_is_unreadable(serve) -> None: + elsewhere = "https://elsewhere.example.test/Patient?p=2" + bundle = { + "resourceType": "Bundle", + "entry": [], + "link": [{"relation": "next", "url": elsewhere}], + } + server = serve(lambda p, q: (200, bundle)) + result = check_duplicate(FhirPatientSource(server.url), WANT) + assert result.decision == "unreadable" and "another host" in result.reason + + +@pytest.mark.parametrize( + ("want", "record"), + [ + # A nickname and a one-field date typo. + ( + {"fname": "Bob", "lname": "Ashby", "DOB": "1950-05-07"}, + rec("Robert", "Ashby", "1950-05-06"), + ), + # A last-name typo and a one-field date typo. + ( + {"fname": "Rosalind", "lname": "Jeswood", "DOB": "1990-04-10"}, + rec("Rosalind", "Jesswood", "1990-04-09"), + ), + ], +) +def test_review_recall_gaps_stop(want, record) -> None: + found = classify(want, record) + assert found is not None and found.match == "close" + + +def test_a_program_maps_its_own_parameter_names() -> None: + from openadapt_flow.ir import Postcondition, PostconditionKind + from openadapt_flow.runtime.replayer import Replayer + from tests.test_no_duplicate_candidate import MARKER + from tests.test_no_duplicate_candidate import _Backend as ListBackend + from tests.test_replayer import FakeVision + + table = TableSource([rec("Rosalind", "Jesswood", "1990-04-09", "7")]) + replayer = Replayer( + ListBackend([]), vision=FakeVision(), duplicate_record_source=table + ) + replayer._current_params = { + "given": "Rosalind", + "family": "Jesswood", + "birth": "1990-04-09", + } + pc = Postcondition( + kind=PostconditionKind.NO_DUPLICATE_CANDIDATE, + text=MARKER, + candidate_rows="tr", + candidate_params=("family",), + candidate_source="record", + candidate_record_fields={"fname": "given", "lname": "family", "DOB": "birth"}, + ) + assert replayer._postcondition_passes(pc, b"", None, {}) is False + assert replayer._last_duplicate_check.decision == "exact" diff --git a/packages/flow/tests/test_duplicate_source_paths.py b/packages/flow/tests/test_duplicate_source_paths.py index bac6ec223..0dea6e284 100644 --- a/packages/flow/tests/test_duplicate_source_paths.py +++ b/packages/flow/tests/test_duplicate_source_paths.py @@ -2,13 +2,16 @@ Qualification, a run, a resume and the lifecycle proof must use the same duplicate check, or a program is admitted under one check and run under -another. The source is resolved inside ``Replayer`` from the run's own effect -verifier (``resolve_duplicate_source``). These tests pin that: +another. The source is resolved by ``Replayer`` from the run's own effect +verifier and the program's pin (``resolve_duplicate_source``). These tests +pin that: - no construction path picks a source of its own (an AST scan of every ``Replayer(...)`` call); - the governed paths all hand the Replayer their effect verifier; - the same verifier gives the same source on every path; +- OpenEMR's REST adapter is used only by a program pinned to the record; +- resolution never crashes on a verifier it doesn't know; - a program pinned to the record check fails closed without a channel. """ @@ -73,9 +76,7 @@ def test_no_construction_path_picks_its_own_duplicate_source() -> None: for rel, fn, call in calls if any(k.arg == "duplicate_record_source" for k in call.keywords) } - # Only the deployment passes the operator's switch (auto or screen), - # never a source object. - assert choosing == {("openadapt_flow/deployment.py", "build_replayer")} + assert choosing == set() def test_every_governed_path_hands_the_replayer_its_effect_verifier() -> None: @@ -87,10 +88,12 @@ def test_every_governed_path_hands_the_replayer_its_effect_verifier() -> None: if (rel, fn) not in GOVERNED: continue names = {k.arg for k in call.keywords} - # ``**kwargs`` (qualify) must carry it; checked below by name. + # ``None`` is ``**kwargs`` (qualify), checked by name below. assert "effect_verifier" in names or None in names, (rel, fn) campaign = (FLOW / "openadapt_flow/qualify/campaign.py").read_text() assert "effect_verifier=verifier" in campaign + # A qualification hook can't swap the duplicate check. + assert "replayer_kwargs may not set duplicate_record_source" in campaign class _Backend: @@ -113,9 +116,18 @@ def _rest() -> RestRecordVerifier: @pytest.mark.parametrize( - ("verifier", "kind"), [(_fhir, FhirPatientSource), (_rest, RestPatientSource)] + ("verifier", "pinned", "kind"), + [ + (_fhir, False, FhirPatientSource), + (_fhir, True, FhirPatientSource), + # OpenEMR's REST adapter only for a program pinned to the record. + (_rest, False, type(None)), + (_rest, True, RestPatientSource), + ], ) -def test_the_same_verifier_gives_the_same_source_on_every_path(verifier, kind) -> None: +def test_the_same_verifier_gives_the_same_source_on_every_path( + verifier: Any, pinned: bool, kind: type +) -> None: from openadapt_flow.deployment import build_replayer from openadapt_flow.runtime.replayer import Replayer from tests.test_replayer import FakeVision @@ -131,15 +143,47 @@ def test_the_same_verifier_gives_the_same_source_on_every_path(verifier, kind) - use_structural=False, ) for replayer in (direct, deployed): - assert isinstance(replayer.duplicate_record_source, kind) - assert replayer.duplicate_record_source.base_url == v.base_url + source = replayer._duplicate_source(pinned) + assert isinstance(source, kind) + if source is not None: + assert source.base_url == v.base_url -def test_no_record_channel_means_the_screen_unless_the_program_pins_record() -> None: +def test_resolution_is_tolerant_and_rest_is_opt_in() -> None: assert resolve_duplicate_source(None) is None - assert resolve_duplicate_source(_fhir(), "screen") is None table = TableSource([]) assert resolve_duplicate_source(None, table) is table + assert resolve_duplicate_source(_rest()) is None + assert isinstance( + resolve_duplicate_source(_rest(), pinned_record=True), RestPatientSource + ) + + class Double(RestRecordVerifier): + """A test double that skips RestRecordVerifier.__init__.""" + + def __init__(self) -> None: + pass + + # No crash at construction; no source, so a pinned program fails closed. + assert resolve_duplicate_source(Double(), pinned_record=True) is None + + class Wrapper: + def __init__(self, inner: Any) -> None: + self.inner = inner + + assert isinstance(resolve_duplicate_source(Wrapper(_fhir())), FhirPatientSource) + + +def test_a_replayer_builds_with_an_unknown_verifier_double() -> None: + from openadapt_flow.runtime.replayer import Replayer + from tests.test_replayer import FakeVision + + class Double(RestRecordVerifier): + def __init__(self) -> None: + pass + + replayer = Replayer(_Backend(), vision=FakeVision(), effect_verifier=Double()) + assert replayer._duplicate_source(True) is None def _check(source: Any, pinned: Any) -> tuple[bool, list[Any]]: @@ -163,8 +207,24 @@ def _check(source: Any, pinned: Any) -> tuple[bool, list[Any]]: def test_a_program_pinned_to_the_record_fails_closed_without_a_channel() -> None: - passed, read = _check("screen", "record") + from openadapt_flow.runtime.replayer import Replayer + from tests.test_replayer import FakeVision + + passed, read = _check(None, "record") assert passed is False and read == [] # never the screen instead + replayer = Replayer(_Backend(), vision=FakeVision()) + pc = Postcondition( + kind=PostconditionKind.NO_DUPLICATE_CANDIDATE, + text="x", + candidate_rows="tr", + candidate_params=("lname",), + candidate_source="record", + ) + replayer._current_params = {} + replayer._postcondition_passes(pc, b"", None, {}) + why = replayer._describe_postcondition(pc) + # The reason the switch policy reads (#1517): a structured, stable phrase. + assert why.startswith("no_duplicate_candidate (record check via none: unreadable") def test_a_program_pinned_to_the_screen_reads_the_screen() -> None: @@ -173,11 +233,13 @@ def test_a_program_pinned_to_the_screen_reads_the_screen() -> None: assert passed is True and read # the list was read; the record wasn't asked -def test_unset_candidate_source_serializes_as_before() -> None: +def test_unset_candidate_fields_serialize_as_before() -> None: pc = Postcondition( kind=PostconditionKind.NO_DUPLICATE_CANDIDATE, text="x", candidate_rows="tr", candidate_params=("lname",), ) - assert "candidate_source" not in pc.model_dump() + dumped = pc.model_dump() + assert "candidate_source" not in dumped + assert "candidate_record_fields" not in dumped From a1003987febe3f557e28d967be2a7ec5677f0f4e Mon Sep 17 00:00:00 2001 From: abrichr Date: Sat, 24 Oct 2026 21:58:18 -0400 Subject: [PATCH 08/13] duplicate check: the halted step carries a structured outcome StepResult.duplicate_check is set whenever no_duplicate_candidate fails: exact or close (a record matched) and candidate (a listed row matched) are about the item; unreadable (the record channel or the screen list could not answer, or a record-pinned check had no channel) is about the program. None otherwise and then omitted, so existing reports serialize unchanged. #1517 classes the problems_only switch from it. Co-Authored-By: Claude Opus 5.5 --- packages/flow/openadapt_flow/ir.py | 10 +++ .../flow/openadapt_flow/runtime/replayer.py | 73 +++++++++++++---- .../flow/tests/test_duplicate_source_paths.py | 80 +++++++++++++++++++ 3 files changed, 146 insertions(+), 17 deletions(-) diff --git a/packages/flow/openadapt_flow/ir.py b/packages/flow/openadapt_flow/ir.py index 01c817ce6..b52e62899 100644 --- a/packages/flow/openadapt_flow/ir.py +++ b/packages/flow/openadapt_flow/ir.py @@ -4397,6 +4397,14 @@ class StepResult(BaseModel): #: on that path alone; None otherwise, and then omitted from the #: serialized result. scroll_target_ready_before: Optional[bool] = None + #: Set when ``no_duplicate_candidate`` failed this step: ``exact`` or + #: ``close`` (a record matched), ``candidate`` (a listed row matched on the + #: screen): about the item; ``unreadable`` (the record channel or the + #: screen list could not answer, or a record-pinned check had no + #: channel): about the program. None otherwise, and then omitted. + duplicate_check: Optional[Literal["exact", "close", "candidate", "unreadable"]] = ( + None + ) @model_serializer(mode="wrap") def _serialize_compatible(self, handler: Any) -> dict[str, Any]: @@ -4415,6 +4423,8 @@ def _serialize_compatible(self, handler: Any) -> dict[str, Any]: data.pop("check_to_click", None) if isinstance(data, dict) and self.scroll_target_ready_before is None: data.pop("scroll_target_ready_before", None) + if isinstance(data, dict) and self.duplicate_check is None: + data.pop("duplicate_check", None) return data diff --git a/packages/flow/openadapt_flow/runtime/replayer.py b/packages/flow/openadapt_flow/runtime/replayer.py index 4f9361111..767544eaa 100644 --- a/packages/flow/openadapt_flow/runtime/replayer.py +++ b/packages/flow/openadapt_flow/runtime/replayer.py @@ -1028,6 +1028,7 @@ def __init__( # of record uses the same check. Only tests and embedders pass one. self._duplicate_requested = duplicate_record_source self._last_duplicate_check: Optional[Any] = None + self._last_duplicate_outcome: Optional[str] = None # ``run``-only, bundle-bound admission capability. The ordinary # ``replay`` path leaves this unset and preserves its permissive # behavior. When present it carries exact identity/effect decisions @@ -7778,13 +7779,15 @@ def _run_switch_surface_step( ok, frame = self._poll_postconditions(step, frame, bundle_dir, start_state) if not ok: frame = self.vision.wait_settled(self.backend) - failed = [ - self._describe_postcondition(pc) + failed_pcs = [ + pc for pc in step.expect if not self._postcondition_passes( pc, frame, bundle_dir, start_state ) ] + self._stamp_duplicate_check(result, failed_pcs) + failed = [self._describe_postcondition(pc) for pc in failed_pcs] result.after_png = self._save_step_png( run_dir, evidence_step_id, "after", frame ) @@ -17581,6 +17584,7 @@ def _check_postconditions( and not self._postcondition_confirms_a_write(step, workflow) ): failed_pcs = self._drift_oracle_rescue(failed_pcs, frame_png, result) + self._stamp_duplicate_check(result, failed_pcs) failed = [self._describe_postcondition(pc) for pc in failed_pcs] return not failed_pcs, frame_png, failed @@ -17723,16 +17727,32 @@ def _malformed_date_params(workflow: Workflow, params: dict[str, Any]) -> list[s return problems def _no_duplicate_candidate(self, pc: Any, frame_png: bytes) -> bool: - """The app's possible-duplicate list has loaded and names no candidate. + """The duplicate check passes: no chart is, or may be, this patient. - A row is a candidate when its words include every word of this run's + Records why it failed in ``self._last_duplicate_outcome`` (``exact``, + ``close``, ``candidate`` or ``unreadable``; None when it passed), which + the halted step's ``StepResult.duplicate_check`` carries. + """ + + outcome = self._duplicate_outcome(pc, frame_png) + self._last_duplicate_outcome = outcome + return outcome is None + + def _duplicate_outcome(self, pc: Any, frame_png: bytes) -> Optional[str]: + """None when the check passes, else why it failed. + + With a record source (``runtime/checks/duplicate_record.py``): the + decision, ``exact``, ``close`` or ``unreadable``. On the screen: the + app's possible-duplicate list has loaded and names no candidate. A + row is a candidate when its words include every word of this run's value of any ``pc.candidate_params`` parameter (case-insensitive). A value that is an ISO date is also compared as a date: every date the row shows, in any format intake reads (``Apr 20, 1944``, ``20.04.1944``, ``04/20/1944``), is normalized and compared, and a row date that reads either way counts when either reading matches. - Fails closed: an unreadable list, a missing or empty parameter, or an - empty list without its "no matches" marker all fail the check. + A listed candidate is ``candidate``. Fails closed as ``unreadable``: + an unreadable list, a missing or empty parameter, or an empty list + without its "no matches" marker. """ params = ( @@ -17740,6 +17760,7 @@ def _no_duplicate_candidate(self, pc: Any, frame_png: bytes) -> bool: or getattr(self, "_run_params", None) or {} ) + self._last_duplicate_check = None pinned = getattr(pc, "candidate_source", None) source = ( None if pinned == "screen" else self._duplicate_source(pinned == "record") @@ -17757,18 +17778,22 @@ def _no_duplicate_candidate(self, pc: Any, frame_png: bytes) -> bool: reason="the program requires the record check and this run has " "no record channel", ) - return False + return "unreadable" if source is not None: - return self._no_duplicate_record(source, params, pc) + if self._no_duplicate_record(source, params, pc): + return None + checked = self._last_duplicate_check + decision = getattr(checked, "decision", "unreadable") + return decision if decision in ("exact", "close") else "unreadable" names = tuple(pc.candidate_params or ()) if not pc.candidate_rows or not names or not pc.text: - return False + return "unreadable" reader = getattr(self.backend, "element_texts", None) if not callable(reader): - return False + return "unreadable" rows = reader(pc.candidate_rows, tuple(pc.candidate_frame_path or ())) if rows is None: - return False + return "unreadable" def words(value: str) -> list[str]: return re.findall(r"[0-9a-z]+", value.casefold()) @@ -17779,7 +17804,7 @@ def words(value: str) -> list[str]: text = runtime_param_text(params[name]) if name in params else "" value = words(text) if not value: - return False + return "unreadable" wanted.append(value) if re.fullmatch(r"\d{4}-\d{2}-\d{2}", text.strip()): wanted_dates.add(text.strip()) @@ -17787,15 +17812,29 @@ def words(value: str) -> list[str]: # Nothing listed: only the list's own "no matches" marker shows # the search finished. if self._dom_text_visible(pc.text): - return True - return self.vision.text_present(frame_png, pc.text) + return None + if self.vision.text_present(frame_png, pc.text): + return None + return "unreadable" # the list never loaded for row in rows: row_words = set(words(row)) if any(all(w in row_words for w in value) for value in wanted): - return False + return "candidate" if wanted_dates and wanted_dates & _row_dates(row): - return False - return True + return "candidate" + return None + + def _stamp_duplicate_check(self, result: Any, failed_pcs: Any) -> None: + """Put the duplicate check's failure on the halted step's result.""" + + if result is None or not any( + getattr(pc.kind, "value", pc.kind) == "no_duplicate_candidate" + for pc in failed_pcs + ): + return + result.duplicate_check = ( + getattr(self, "_last_duplicate_outcome", None) or "unreadable" + ) def _duplicate_source(self, pinned_record: bool = False) -> Any: """This run's duplicate-check record source, or None (the screen).""" diff --git a/packages/flow/tests/test_duplicate_source_paths.py b/packages/flow/tests/test_duplicate_source_paths.py index 0dea6e284..ae251d8fd 100644 --- a/packages/flow/tests/test_duplicate_source_paths.py +++ b/packages/flow/tests/test_duplicate_source_paths.py @@ -243,3 +243,83 @@ def test_unset_candidate_fields_serialize_as_before() -> None: dumped = pc.model_dump() assert "candidate_source" not in dumped assert "candidate_record_fields" not in dumped + + +# -- the structured outcome on the halted step (#1517 reads it) ----------------- + + +def _halted(source: Any, rows: Any, pinned: Any = None) -> Any: + from openadapt_flow.ir import StepResult + from openadapt_flow.runtime.replayer import Replayer + from tests.test_no_duplicate_candidate import MARKER + from tests.test_no_duplicate_candidate import _Backend as ListBackend + from tests.test_replayer import FakeVision + + replayer = Replayer( + ListBackend(rows, marker=False), + vision=FakeVision(), + duplicate_record_source=source, + ) + replayer._current_params = {"fname": "Ada", "lname": "Quill", "DOB": "1984-03-07"} + pc = Postcondition( + kind=PostconditionKind.NO_DUPLICATE_CANDIDATE, + text=MARKER, + candidate_rows="tr.oneresult", + candidate_frame_path=("#modalframe",), + candidate_params=("lname", "DOB"), + candidate_source=pinned, + ) + result = StepResult(step_id="s", intent="x", ok=False) + failed = [] if replayer._postcondition_passes(pc, b"", None, {}) else [pc] + replayer._stamp_duplicate_check(result, failed) + return result + + +@pytest.mark.parametrize( + ("source", "rows", "pinned", "outcome"), + [ + ( + TableSource([PatientRecord("1", "Ada", "Quill", "1984-03-07")]), + [], + None, + "exact", + ), + ( + TableSource([PatientRecord("2", "Ada", "Okafor", "1984-03-07")]), + [], + None, + "close", + ), + (None, [], "record", "unreadable"), # pinned, no channel + (None, ["5\tQuill, Ana\t1961-01-01"], "screen", "candidate"), + (None, None, "screen", "unreadable"), # the list can't be read + (None, [], "screen", "unreadable"), # never loaded (no marker) + ], +) +def test_the_halted_step_carries_the_duplicate_outcome( + source, rows, pinned, outcome +) -> None: + result = _halted(source, rows, pinned) + assert result.duplicate_check == outcome + assert result.model_dump()["duplicate_check"] == outcome + + +def test_an_unreadable_record_channel_is_unreadable() -> None: + class Broken: + channel = "fhir" + + def by_birthdate(self, dob: str) -> list[PatientRecord]: + raise RuntimeError("down") + + assert _halted(Broken(), []).duplicate_check == "unreadable" + + +def test_a_passing_or_unrelated_step_serializes_as_before() -> None: + from openadapt_flow.ir import StepResult + + result = _halted(TableSource([]), []) + assert result.duplicate_check is None + assert ( + "duplicate_check" + not in StepResult(step_id="s", intent="x", ok=True).model_dump() + ) From b464cd10602611eb2d37b30e764b98073fa5ed7b Mon Sep 17 00:00:00 2001 From: abrichr Date: Sat, 24 Oct 2026 23:04:18 -0400 Subject: [PATCH 09/13] duplicate check: live OpenEMR probe and what it found benchmark/dup_check/live.py seeds synthetic charts on the pinned fixture and probes OpenEMR's REST and FHIR patient search. REST honors _limit and gives links.next, but on OpenEMR's configured site address, which the check refuses as another origin (fail closed): REST stays opt-in. FHIR gives no next link and a total equal to the rows returned; without _count it returned every row (120 tested). The check decided 6 of 6 referrals right through FHIR and REST at 500 a page. Raw output is private (openadapt-corpus#18). Co-Authored-By: Claude Opus 5.5 --- packages/flow/benchmark/dup_check/README.md | 39 +++ packages/flow/benchmark/dup_check/live.py | 306 ++++++++++++++++++++ packages/flow/docs/DUPLICATE_CHECK.md | 11 + 3 files changed, 356 insertions(+) create mode 100644 packages/flow/benchmark/dup_check/live.py diff --git a/packages/flow/benchmark/dup_check/README.md b/packages/flow/benchmark/dup_check/README.md index 9953c07d6..e25e1b431 100644 --- a/packages/flow/benchmark/dup_check/README.md +++ b/packages/flow/benchmark/dup_check/README.md @@ -90,6 +90,45 @@ bring back the screen's `shared_dob` false stops (33 here). shared contact details for a near date, or one for the same date. That narrowing is tuned to this stream. +## Live OpenEMR (2026-10-24) + +`live.py` on the pinned OpenEMR 8.0.0.3 fixture. It seeded 23 synthetic +charts, 13 of them born on one day, and probed both channels. Raw output +(`probe.json`, `decisions.json`) is private, in openadapt-corpus +`reliability_recipes/verification_coverage/live-2026-10-24/dup_check/`. + +What OpenEMR does: + +| | REST `/api/patient` | FHIR `Patient` | +|---|---|---| +| page size | `_limit` honored | `_count` honored | +| next page | `links.next`, but built on OpenEMR's configured site address, not the address the request went to; no total | no next link at all, and `Bundle.total` equals the rows returned, so `_count=5` silently returns 5 of 13 | +| with no page size | all 120 of 120 rows sharing a birth date | all 120 of 120 | +| `lname` / `family` | prefix match (`Hartwell` finds `Hartwell1`...) | prefix match | +| birth name | not exposed | a birth name isn't returned as an old name; searching it finds nothing | + +The check's decisions on six referrals with known answers: +- exact; +- a married name; +- a nickname; +- a swapped day and month; +- a new patient sharing only the birth date; +- a namesake. + +| channel | right | notes | +|---|---:|---| +| FHIR, as the check sends it (no `_count`) | 6 / 6 | | +| REST, 500 rows a page | 6 / 6 | | +| REST, 5 rows a page | 4 / 6 | the two that needed a second page were `unreadable`: the next link points to the site address, another origin, so the check refused it (fail closed) | + +So: +- REST stays opt-in. Its paging only works where OpenEMR's site address is + the address the check reads. +- `FhirPatientSource` never sends `_count`. OpenEMR returned all rows without + it (up to 120 tested), and the total can't reveal a cap, so a larger result + set than that is untested. +- Prior names are only searchable through the SQL adapter. + ## How the set was built For every existing chart, `plant` inserts duplicates at seeded positions: diff --git a/packages/flow/benchmark/dup_check/live.py b/packages/flow/benchmark/dup_check/live.py new file mode 100644 index 000000000..e81b7a185 --- /dev/null +++ b/packages/flow/benchmark/dup_check/live.py @@ -0,0 +1,306 @@ +"""The record duplicate check against live OpenEMR: search semantics and paging. + + python -m benchmark.dup_check.live --out DIR + +Needs the fixture lease and ``OPENEMR_FIXTURE_STATE`` / ``OPENEMR_UI_PORT`` / +``OPENEMR_API_PORT``. No model calls. Resets the fixture first, then seeds 23 +synthetic charts through OpenEMR's REST API, 13 of them born on one day, +so a small page size forces several pages. + +It records what OpenEMR's channels actually do (``probe.json``): + +- REST ``/api/patient``: whether ``_limit`` caps a page, whether a reply + carries ``links.next`` or a total, the body's keys, and whether ``lname`` + matches exactly or by prefix; +- FHIR ``Patient``: whether ``_count`` pages with ``Bundle.link[next]`` and + ``Bundle.total``, whether ``family`` matches by prefix, and whether a stored + birth name comes back as an old name; + +and then runs ``check_duplicate`` through each channel on six referrals with +known answers (``decisions.json``). +""" + +from __future__ import annotations + +import argparse +import json +import sys +from pathlib import Path +from typing import Any + +HERE = Path(__file__).resolve().parent +sys.path.insert(0, str(HERE.parent)) + +from openadapt_flow.runtime.checks.duplicate_record import ( # noqa: E402 + FhirPatientSource, + RestPatientSource, + check_duplicate, +) + +DOB = "1977-05-14" +SCOPE = "openid api:oemr api:fhir user/patient.crus user/Patient.read user/Patient.rs" + + +def _people() -> list[dict[str, str]]: + base = { + "title": "Ms.", + "sex": "Female", + "street": "1 PROBE ROAD", + "city": "EXAMPLETON", + "state": "MA", + "postal_code": "02139", + "country_code": "USA", + } + out = [] + for i in range(13): # one shared birth day: forces pages at a small size + out.append( + { + **base, + "fname": f"Shared{i}", + "lname": f"Dayborn{i}", + "DOB": DOB, + "phone_home": f"555-06{i:02d}", + "email": f"shared.{i}@example.invalid", + } + ) + for i in range(6): # one last-name stem: prefix or exact matching + out.append( + { + **base, + "fname": f"Stem{i}", + "lname": f"Hartwell{'' if i == 0 else i}", + "DOB": f"1960-01-{i + 10:02d}", + "phone_home": f"555-07{i:02d}", + "email": f"stem.{i}@example.invalid", + } + ) + out.append( + { + **base, + "fname": "Rosalind", + "lname": "Okafor", + "DOB": "1990-04-09", + "phone_home": "617-555-0142", + "email": "r.okafor@example.invalid", + } + ) + out.append( + { + **base, + "fname": "Robert", + "lname": "Ashby", + "DOB": "1950-02-03", + "phone_home": "555-0800", + "email": "r.ashby@example.invalid", + } + ) + out.append( + { + **base, + "fname": "Ada", + "lname": "Quill", + "DOB": "1984-03-07", + "phone_home": "555-0801", + "email": "a.quill@example.invalid", + } + ) + out.append( + { + **base, + "fname": "Mabel", + "lname": "Ortiz", + "DOB": "1990-01-21", + "phone_home": "555-0802", + "email": "m.ortiz@example.invalid", + } + ) + return out + + +#: (referral, expected decision) +REFERRALS = [ + ({"fname": "Ada", "lname": "Quill", "DOB": "1984-03-07"}, "exact"), + ( + {"fname": "Rosalind", "lname": "Jesswood", "DOB": "1990-04-09"}, + "close", + ), # married + ({"fname": "Bob", "lname": "Ashby", "DOB": "1950-02-03"}, "close"), # nickname + ( + {"fname": "Ada", "lname": "Quill", "DOB": "1984-07-03"}, + "close", + ), # swapped day/month + ({"fname": "Nadia", "lname": "Fresh", "DOB": DOB}, "none"), # shares only the day + ({"fname": "Tomas", "lname": "Hartwell", "DOB": "1999-12-01"}, "none"), # namesake +] + + +def main() -> int: + from verification_coverage.independent import live # the fixture helpers + + parser = argparse.ArgumentParser(description=__doc__.splitlines()[0]) + parser.add_argument("--out", type=Path, required=True) + args = parser.parse_args() + args.out.mkdir(parents=True, exist_ok=True) + fx = live.fx() + live.reset() + clients = json.loads(fx.clients_path.read_text()) + live.sql( + f"UPDATE openemr.oauth_clients SET scope = {live.q(SCOPE)} " + f"WHERE client_id = {live.q(clients['actor_client_id'])};" + ) + import httpx + + values = fx._runtime_values() + session = httpx.Client(verify=False, timeout=30) + resp = session.post( + f"{fx.api_base_url}/oauth2/default/token", + data={ + "grant_type": "password", + "client_id": clients["actor_client_id"], + "scope": SCOPE, + "user_role": "users", + "username": values["OPENEMR_ACTOR_USER"], + "password": values["OPENEMR_ACTOR_PASSWORD"], + }, + ) + if resp.status_code != 200: + raise SystemExit(f"token: HTTP {resp.status_code}") + token = resp.json()["access_token"] + auth = {"Authorization": f"Bearer {token}"} + api = f"{fx.api_base_url}/apis/default/api" + fhir = f"{fx.api_base_url}/apis/default/fhir" + for person in _people(): + r = session.post(f"{api}/patient", json=person, headers=auth) + if r.status_code not in (200, 201): + raise SystemExit(f"seed: HTTP {r.status_code} {r.text[:200]}") + # A married chart keeps her birth name, as OpenEMR's demographics allow. + live.sql( + "UPDATE openemr.patient_data SET birth_fname = 'Rosalind', " + "birth_lname = 'Jesswood' WHERE email = 'r.okafor@example.invalid'" + ) + + probe: dict[str, Any] = {} + + def rest(params: dict[str, Any]) -> dict[str, Any]: + r = session.get(f"{api}/patient", params=params, headers=auth) + body = ( + r.json() + if r.headers.get("content-type", "").startswith("application/json") + else {} + ) + data = body.get("data") if isinstance(body, dict) else None + return { + "status": r.status_code, + "keys": sorted(body) if isinstance(body, dict) else type(body).__name__, + "rows": len(data) if isinstance(data, list) else None, + "links": body.get("links") if isinstance(body, dict) else None, + "lnames": sorted({d.get("lname") for d in data or []}) + if isinstance(data, list) + else None, + } + + probe["rest_dob_all"] = rest({"DOB": DOB}) + probe["rest_dob_limit5"] = rest({"DOB": DOB, "_limit": 5}) + probe["rest_dob_limit5_offset5"] = rest({"DOB": DOB, "_limit": 5, "_offset": 5}) + probe["rest_lname_exact"] = rest({"lname": "Hartwell"}) + probe["rest_no_match"] = rest({"DOB": "1801-01-01"}) + + def fhir_search(params: dict[str, Any]) -> dict[str, Any]: + pages, total, ids, url, q = 0, None, [], f"{fhir}/Patient", dict(params) + links_seen = [] + while url and pages < 20: + r = session.get( + url, params=q, headers={**auth, "Accept": "application/fhir+json"} + ) + pages += 1 + if r.status_code != 200: + return {"status": r.status_code, "text": r.text[:200]} + b = r.json() + total = b.get("total", total) + ids += [e["resource"]["id"] for e in b.get("entry", []) or []] + nxt = [ + link["url"] + for link in b.get("link", []) + if link.get("relation") == "next" + ] + links_seen.append([link.get("relation") for link in b.get("link", [])]) + url, q = (nxt[0] if nxt else None), None + return { + "pages": pages, + "total": total, + "read": len(ids), + "unique": len(set(ids)), + "link_relations": links_seen[:3], + } + + probe["fhir_birthdate"] = fhir_search({"birthdate": DOB}) + probe["fhir_birthdate_count5"] = fhir_search({"birthdate": DOB, "_count": 5}) + probe["fhir_family_prefix"] = fhir_search({"family": "Hartwell"}) + r = session.get( + f"{fhir}/Patient", + params={"family": "Jesswood"}, + headers={**auth, "Accept": "application/fhir+json"}, + ) + b = r.json() if r.status_code == 200 else {} + probe["fhir_family_birth_name"] = { + "status": r.status_code, + "total": b.get("total"), + "names": [e["resource"].get("name") for e in b.get("entry", []) or []][:2], + } + (args.out / "probe.json").write_text( + json.dumps(probe, indent=2, sort_keys=True) + "\n" + ) + print(json.dumps(probe, indent=1, sort_keys=True)[:4000]) + + decisions = [] + channels = { + "fhir_page5": lambda: FhirPatientSource( + fhir, + token=lambda: token, + verify_tls=False, + session=httpx.Client(verify=False, timeout=30), + ), + "rest_page5": lambda: RestPatientSource( + fx.api_base_url, + headers=auth, + page_size=5, + session=httpx.Client(verify=False, timeout=30), + ), + "rest_page500": lambda: RestPatientSource( + fx.api_base_url, + headers=auth, + session=httpx.Client(verify=False, timeout=30), + ), + } + for name, make in channels.items(): + for want, expected in REFERRALS: + result = check_duplicate(make(), want) + decisions.append( + { + "channel": name, + "referral": want, + "expected": expected, + "decision": result.decision, + "reason": result.reason, + "pages": result.pages, + "candidates": [c.why for c in result.candidates], + } + ) + print( + name, + want["fname"], + want["lname"], + want["DOB"], + "expected", + expected, + "->", + result.decision, + "|", + result.reason[:90], + ) + (args.out / "decisions.json").write_text(json.dumps(decisions, indent=2) + "\n") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/packages/flow/docs/DUPLICATE_CHECK.md b/packages/flow/docs/DUPLICATE_CHECK.md index 6186804ad..22c8f5b86 100644 --- a/packages/flow/docs/DUPLICATE_CHECK.md +++ b/packages/flow/docs/DUPLICATE_CHECK.md @@ -145,6 +145,17 @@ Sharing only a last name, or only a date of birth, doesn't stop the item. problem with the program or its channel. `exact` and `close` are about the item. +**On OpenEMR 8.0.0.3** (measured live, `benchmark/dup_check/README.md`): +- REST pages with `links.next`, but the link carries OpenEMR's configured site + address. The check refuses a link to another origin, so on a deployment + whose site address differs from the address the check reads, a + multi-page REST answer is `unreadable`. +- FHIR returns no next link, and its `Bundle.total` equals the rows returned. + The check sends no `_count`, and OpenEMR returned every row (up to 120 + tested). +- Both match last names by prefix. A birth name is not searchable through + FHIR. + Not catchable from the referral alone: a person whose name changed, who moved, and whose date of birth is mistyped or merely shared. Nothing the referral carries ties them to the chart; that needs an MRN, an insurance id, or a From 647af767d6267272c9e214ec73265d1ff03b66e7 Mon Sep 17 00:00:00 2001 From: abrichr Date: Sat, 24 Oct 2026 23:06:45 -0400 Subject: [PATCH 10/13] duplicate check: a program can choose FHIR beside an OpenEMR REST record check candidate_channel: fhir with a RestRecordVerifier on .../apis/default/api/patient searches OpenEMR's FHIR API on the same server (.../apis/default/fhir) with the record check's bearer token; rest uses only the REST adapter. Live, REST's next-page links carry OpenEMR's configured site address, so FHIR is the channel that answers completely there. Co-Authored-By: Claude Opus 5.5 --- packages/flow/docs/DUPLICATE_CHECK.md | 9 ++++ packages/flow/openadapt_flow/ir.py | 6 +++ .../runtime/checks/duplicate_record.py | 42 +++++++++++++++++-- .../flow/openadapt_flow/runtime/replayer.py | 11 ++++- .../flow/tests/test_duplicate_source_paths.py | 24 +++++++++++ 5 files changed, 87 insertions(+), 5 deletions(-) diff --git a/packages/flow/docs/DUPLICATE_CHECK.md b/packages/flow/docs/DUPLICATE_CHECK.md index 22c8f5b86..82c2d223b 100644 --- a/packages/flow/docs/DUPLICATE_CHECK.md +++ b/packages/flow/docs/DUPLICATE_CHECK.md @@ -95,6 +95,15 @@ construction to keep it so. - `screen`: the on-screen list, even when a channel exists. - unset: the FHIR channel when the run has one, else the screen. +**Choosing the channel.** `candidate_channel` picks one: +- `fhir` with an OpenEMR REST record check (`.../apis/default/api/patient`) + searches OpenEMR's FHIR API on the same server (`.../apis/default/fhir`), + with the record check's bearer token. That token's client then needs the + FHIR read scope, `api:fhir user/Patient.read`. Without it every search is + refused, and the check is `unreadable`. +- `rest` uses only the REST adapter. +- Unset, the record check's own channel decides. + **Parameter names.** The check reads `fname`, `lname`, `DOB`, and when present `mrn`, `phone_home`, `email` and `street`: OpenEMR's referral names. A program for another app maps its own with `candidate_record_fields`, for diff --git a/packages/flow/openadapt_flow/ir.py b/packages/flow/openadapt_flow/ir.py index b52e62899..619063b3f 100644 --- a/packages/flow/openadapt_flow/ir.py +++ b/packages/flow/openadapt_flow/ir.py @@ -524,6 +524,11 @@ class Postcondition(BaseModel): #: each field the record check reads (``fname``, ``lname``, ``DOB``, #: ``mrn``, ``phone_home``, ``email``, ``street``). Unset: those names. candidate_record_fields: Optional[dict[str, str]] = None + #: NO_DUPLICATE_CANDIDATE, record source: which channel to search. + #: ``fhir`` with an OpenEMR REST record check searches OpenEMR's FHIR API + #: on the same server; ``rest``, OpenEMR's REST API only. Unset: the + #: channel of the run's record check. + candidate_channel: Optional[Literal["fhir", "rest"]] = None @model_serializer(mode="wrap") def _serialize_compatible(self, handler: Any) -> dict[str, Any]: @@ -541,6 +546,7 @@ def _serialize_compatible(self, handler: Any) -> dict[str, Any]: "candidate_params", "candidate_source", "candidate_record_fields", + "candidate_channel", ): if getattr(self, name) is None: data.pop(name, None) diff --git a/packages/flow/openadapt_flow/runtime/checks/duplicate_record.py b/packages/flow/openadapt_flow/runtime/checks/duplicate_record.py index 6f3e71d29..1993e7acf 100644 --- a/packages/flow/openadapt_flow/runtime/checks/duplicate_record.py +++ b/packages/flow/openadapt_flow/runtime/checks/duplicate_record.py @@ -976,7 +976,10 @@ def _unwrap(verifier: Any) -> Any: def source_for_verifier( - verifier: Any, *, allow_rest: bool = False + verifier: Any, + *, + allow_rest: bool = False, + channel: Optional[Literal["fhir", "rest"]] = None, ) -> Optional[PatientSource]: """The patient search on the same channel as a run's record check. @@ -985,6 +988,12 @@ def source_for_verifier( gives the OpenEMR REST adapter, but only with ``allow_rest`` (a program pinned to the record check). Anything else, or a verifier missing the attributes these need, gives None. + + ``channel`` (the program's ``candidate_channel``) chooses: ``fhir`` with + an OpenEMR REST verifier searches OpenEMR's FHIR API on the same server + (``.../apis//fhir``) with the verifier's bearer token, which then + needs the FHIR read scope (``api:fhir user/Patient.read``); ``rest`` + uses only the REST adapter. """ from openadapt_flow.runtime.effects import FhirEffectVerifier @@ -992,6 +1001,8 @@ def source_for_verifier( verifier = _unwrap(verifier) try: + if channel == "rest" and not isinstance(verifier, RestRecordVerifier): + return None if isinstance(verifier, FhirEffectVerifier): access_token = getattr(verifier, "access_token", None) token_source = getattr(verifier, "token_source", None) @@ -1008,6 +1019,25 @@ def token() -> Optional[str]: timeout_s=max(float(getattr(verifier, "timeout_s", 10.0)), 10.0), ) records_path = str(getattr(verifier, "records_path", "") or "") + if ( + channel == "fhir" + and isinstance(verifier, RestRecordVerifier) + and "/api/patient" in records_path + ): + # OpenEMR serves FHIR beside its REST API: .../apis/default/fhir. + site = records_path.split("?", 1)[0].split("/api/patient", 1)[0] + bearer = str( + (getattr(verifier, "headers", None) or {}).get("Authorization", "") + ) + token = bearer.removeprefix("Bearer ").strip() + return FhirPatientSource( + f"{verifier.base_url}{site}/fhir", + token=(lambda: token) if token else None, + session=verifier._get_session(), + timeout_s=max(float(getattr(verifier, "timeout_s", 10.0)), 10.0), + ) + if channel == "fhir": + return None if ( allow_rest and isinstance(verifier, RestRecordVerifier) @@ -1031,7 +1061,11 @@ def token() -> Optional[str]: def resolve_duplicate_source( - effect_verifier: Any, requested: Any = AUTO, *, pinned_record: bool = False + effect_verifier: Any, + requested: Any = AUTO, + *, + pinned_record: bool = False, + channel: Optional[Literal["fhir", "rest"]] = None, ) -> Any: """The one place a run's duplicate-check source is decided. @@ -1049,4 +1083,6 @@ def resolve_duplicate_source( return requested if effect_verifier is None: return None - return source_for_verifier(effect_verifier, allow_rest=pinned_record) + return source_for_verifier( + effect_verifier, allow_rest=pinned_record, channel=channel + ) diff --git a/packages/flow/openadapt_flow/runtime/replayer.py b/packages/flow/openadapt_flow/runtime/replayer.py index 767544eaa..c3f54c966 100644 --- a/packages/flow/openadapt_flow/runtime/replayer.py +++ b/packages/flow/openadapt_flow/runtime/replayer.py @@ -17763,7 +17763,11 @@ def _duplicate_outcome(self, pc: Any, frame_png: bytes) -> Optional[str]: self._last_duplicate_check = None pinned = getattr(pc, "candidate_source", None) source = ( - None if pinned == "screen" else self._duplicate_source(pinned == "record") + None + if pinned == "screen" + else self._duplicate_source( + pinned == "record", getattr(pc, "candidate_channel", None) + ) ) if pinned == "record" and source is None: # The program was qualified with the record check; without a @@ -17836,7 +17840,9 @@ def _stamp_duplicate_check(self, result: Any, failed_pcs: Any) -> None: getattr(self, "_last_duplicate_outcome", None) or "unreadable" ) - def _duplicate_source(self, pinned_record: bool = False) -> Any: + def _duplicate_source( + self, pinned_record: bool = False, channel: Any = None + ) -> Any: """This run's duplicate-check record source, or None (the screen).""" from openadapt_flow.runtime.checks.duplicate_record import ( @@ -17847,6 +17853,7 @@ def _duplicate_source(self, pinned_record: bool = False) -> Any: self.effect_verifier, getattr(self, "_duplicate_requested", "auto"), pinned_record=pinned_record, + channel=channel, ) def _no_duplicate_record( diff --git a/packages/flow/tests/test_duplicate_source_paths.py b/packages/flow/tests/test_duplicate_source_paths.py index ae251d8fd..e18dd8b71 100644 --- a/packages/flow/tests/test_duplicate_source_paths.py +++ b/packages/flow/tests/test_duplicate_source_paths.py @@ -323,3 +323,27 @@ def test_a_passing_or_unrelated_step_serializes_as_before() -> None: "duplicate_check" not in StepResult(step_id="s", intent="x", ok=True).model_dump() ) + + +def test_a_program_can_choose_fhir_beside_an_openemr_rest_check() -> None: + source = resolve_duplicate_source(_rest(), pinned_record=True, channel="fhir") + assert isinstance(source, FhirPatientSource) + assert source.base_url == "https://emr.example.test/apis/default/fhir" + assert source._headers()["Authorization"] == "Bearer t" + # Asked for REST with a FHIR check, or FHIR with an unrelated REST read: + # no source, so a record-pinned program fails closed. + assert resolve_duplicate_source(_fhir(), pinned_record=True, channel="rest") is None + other = RestRecordVerifier("https://x.test", records_path="/api/db") + assert resolve_duplicate_source(other, pinned_record=True, channel="fhir") is None + + +def test_the_channel_choice_serializes_only_when_set() -> None: + pc = Postcondition( + kind=PostconditionKind.NO_DUPLICATE_CANDIDATE, + text="x", + candidate_rows="tr", + candidate_params=("lname",), + candidate_source="record", + candidate_channel="fhir", + ) + assert pc.model_dump()["candidate_channel"] == "fhir" From b69c1f80df72fea9f8b94621003624e211b44394 Mon Sep 17 00:00:00 2001 From: abrichr Date: Sat, 24 Oct 2026 23:25:16 -0400 Subject: [PATCH 11/13] duplicate check: per-item results out of the tree The offline measurement's per-item rows (about 8.5k lines) move to the private openadapt-corpus; the README keeps the per-kind tables. measure.py now requires --out so it never writes into the repository. Co-Authored-By: Claude Opus 5.5 --- packages/flow/benchmark/dup_check/README.md | 6 +- packages/flow/benchmark/dup_check/measure.py | 3 +- .../dup_check/results/seed7-500.json | 6093 ----------------- .../dup_check/results/soak-seed7-55.json | 2436 ------- packages/flow/public-artifacts.json | 2 - 5 files changed, 6 insertions(+), 8534 deletions(-) delete mode 100644 packages/flow/benchmark/dup_check/results/seed7-500.json delete mode 100644 packages/flow/benchmark/dup_check/results/soak-seed7-55.json diff --git a/packages/flow/benchmark/dup_check/README.md b/packages/flow/benchmark/dup_check/README.md index e25e1b431..a2265980c 100644 --- a/packages/flow/benchmark/dup_check/README.md +++ b/packages/flow/benchmark/dup_check/README.md @@ -7,10 +7,12 @@ of record instead (`runtime/checks/duplicate_record.py`) against the screen rule, on the same stream and the same patient table. ```bash -python -m benchmark.dup_check.measure # writes results/*.json +python -m benchmark.dup_check.measure --out DIR # per-item rows, kept out of the repo ``` -No app, no model calls. `measure.py` says exactly what each check sees. The +The per-item rows of the run below are private, in openadapt-corpus +(`reliability_recipes/verification_coverage/dup_check_offline-2026-10-24/`); +this README keeps the aggregate tables. No app, no model calls. `measure.py` says exactly what each check sees. The record check reads an in-memory table with FHIR search semantics (`TableSource`): a case-insensitive name prefix and an exact birth date. diff --git a/packages/flow/benchmark/dup_check/measure.py b/packages/flow/benchmark/dup_check/measure.py index f3dc544eb..512a85876 100644 --- a/packages/flow/benchmark/dup_check/measure.py +++ b/packages/flow/benchmark/dup_check/measure.py @@ -492,7 +492,8 @@ def _count(rows: list[dict[str, Any]], key: str = "exception") -> dict[str, int] def main() -> int: parser = argparse.ArgumentParser(description=__doc__.splitlines()[0]) - parser.add_argument("--out", type=Path, default=HERE / "results") + # Per-item rows are raw evidence: write them outside the repository. + parser.add_argument("--out", type=Path, required=True) parser.add_argument("--long-items", type=int, default=500) args = parser.parse_args() args.out.mkdir(parents=True, exist_ok=True) diff --git a/packages/flow/benchmark/dup_check/results/seed7-500.json b/packages/flow/benchmark/dup_check/results/seed7-500.json deleted file mode 100644 index dc73843eb..000000000 --- a/packages/flow/benchmark/dup_check/results/seed7-500.json +++ /dev/null @@ -1,6093 +0,0 @@ -{ - "rows": [ - { - "exception": "pl_married_moved", - "item": "pl-mm-11", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00001", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00002", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00003", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00004", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "heldout_last_typo", - "item": "held-l-9", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "shared_dob", - "item": "ref-00005", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "malformed_dob", - "item": "ref-00006", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00007", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "missing_required", - "item": "ref-00008", - "record": "unreadable", - "record_why": [], - "right": "no chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00009", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00010", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00011", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "concurrent_same", - "item": "ref-00012", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-8", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "concurrent_other", - "item": "ref-00013", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00014", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00015", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00016", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00017", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00018", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "malformed_dob", - "item": "ref-00019", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00020", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "dup_resend", - "item": "ref-00021", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00022", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00023", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "r2_name_swap", - "item": "r2-sw-6", - "record": "close", - "record_why": [ - "first and last name swapped" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "ambiguous_same_person", - "item": "ref-00024", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00025", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "malformed_case", - "item": "ref-00026", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "common_surname", - "item": "ref-00027", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00028", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "concurrent_same", - "item": "ref-00029", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "common_surname", - "item": "ref-00030", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00031", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "missing_optional", - "item": "ref-00032", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "common_surname", - "item": "ref-00033", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "dup_existing", - "item": "ref-00034", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00035", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00036", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00037", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "slow_save", - "item": "ref-00038", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-0", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00039", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-4", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "shared_dob", - "item": "ref-00040", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "shared_dob", - "item": "ref-00041", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00042", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00043", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "dup_resend", - "item": "ref-00044", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "dup_existing", - "item": "ref-00045", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth", - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "session_expiry", - "item": "ref-00046", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-1", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00047", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "r2_name_swap", - "item": "r2-sw-11", - "record": "close", - "record_why": [ - "first and last name swapped" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00048", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00049", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "planted_married", - "item": "plant-m-10", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_initial", - "item": "pl-in-1", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_last_typo", - "item": "held-l-0", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00050", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00051", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "dup_existing", - "item": "ref-00052", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth", - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "missing_optional", - "item": "ref-00053", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "common_surname", - "item": "ref-00054", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-10", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-6", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "missing_required", - "item": "ref-00055", - "record": "unreadable", - "record_why": [], - "right": "no chart", - "screen": "stop" - }, - { - "exception": "shared_dob", - "item": "ref-00056", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00057", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-10", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-5", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-2", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "failed_save", - "item": "ref-00058", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-1", - "record": "close", - "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "rv_last_typo_dob_typo", - "item": "rv-lt-0", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-5", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-2", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00059", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "rv_last_typo_dob_typo", - "item": "rv-lt-1", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00060", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "rv_last_typo_dob_typo", - "item": "rv-lt-5", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00061", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "shared_dob", - "item": "ref-00062", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "shared_dob", - "item": "ref-00063", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "dup_existing", - "item": "ref-00064", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth", - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00065", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "heldout_first_typo", - "item": "held-f-4", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00066", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "r2_name_swap", - "item": "r2-sw-7", - "record": "close", - "record_why": [ - "first and last name swapped" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "session_expiry", - "item": "ref-00067", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "r2_year_transposed", - "item": "r2-ty-8", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00068", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-4", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "planted_married", - "item": "plant-m-1", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00069", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "dup_resend", - "item": "ref-00070", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00071", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00072", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "concurrent_other", - "item": "ref-00073", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-3", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-0", - "record": "close", - "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00074", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-11", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00075", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "shared_dob", - "item": "ref-00076", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "rv_last_typo_dob_typo", - "item": "rv-lt-11", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00077", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00078", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00079", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "pl_married_moved", - "item": "pl-mm-5", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "common_surname", - "item": "ref-00080", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "r2_year_transposed", - "item": "r2-ty-10", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00081", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00082", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "ambiguous_same_person", - "item": "ref-00083", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_first_typo", - "item": "held-f-11", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00084", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-0", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00085", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "concurrent_same", - "item": "ref-00086", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_first_typo", - "item": "held-f-1", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00087", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "dup_resend", - "item": "ref-00088", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_initial", - "item": "pl-in-2", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00089", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "shared_dob", - "item": "ref-00090", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00091", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "missing_required", - "item": "ref-00092", - "record": "unreadable", - "record_why": [], - "right": "no chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00093", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "dup_existing", - "item": "ref-00094", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_year_transposed", - "item": "r2-ty-2", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_name_swap", - "item": "r2-sw-0", - "record": "close", - "record_why": [ - "first and last name swapped" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_initial", - "item": "pl-in-6", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "shared_dob", - "item": "ref-00095", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00096", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "shared_dob", - "item": "ref-00097", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-9", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00098", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00099", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00100", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00101", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00102", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "heldout_last_typo", - "item": "held-l-4", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "missing_optional", - "item": "ref-00103", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "missing_optional", - "item": "ref-00104", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "planted_married", - "item": "plant-m-7", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00105", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00106", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00107", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "shared_dob", - "item": "ref-00108", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "heldout_last_typo", - "item": "held-l-2", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "dup_resend", - "item": "ref-00109", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-7", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00110", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-11", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00111", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00112", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "dup_existing", - "item": "ref-00113", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "concurrent_other", - "item": "ref-00114", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-9", - "record": "close", - "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "ambiguous_same_person", - "item": "ref-00115", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00116", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "malformed_dob", - "item": "ref-00117", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "r2_name_swap", - "item": "r2-sw-1", - "record": "close", - "record_why": [ - "first and last name swapped" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "common_surname", - "item": "ref-00118", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00119", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00120", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "pl_married_moved", - "item": "pl-mm-8", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00121", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "r2_name_swap", - "item": "r2-sw-8", - "record": "close", - "record_why": [ - "first and last name swapped" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00122", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "planted_hyphenated", - "item": "plant-h-11", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00123", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-1", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "dup_resend", - "item": "ref-00124", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00125", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00126", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00127", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "shared_dob", - "item": "ref-00128", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00129", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00130", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00131", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "shared_dob", - "item": "ref-00132", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00133", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "pl_married_moved", - "item": "pl-mm-4", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "planted_hyphenated", - "item": "plant-h-2", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00134", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "session_expiry", - "item": "ref-00135", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "r2_year_transposed", - "item": "r2-ty-7", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00136", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00137", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00138", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00139", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "common_surname", - "item": "ref-00140", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "session_expiry", - "item": "ref-00141", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "rv_last_typo_dob_typo", - "item": "rv-lt-8", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "pl_married_moved", - "item": "pl-mm-6", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "slow_save", - "item": "ref-00142", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00143", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "planted_married", - "item": "plant-m-9", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00144", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "slow_save", - "item": "ref-00145", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-10", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_year_transposed", - "item": "r2-ty-4", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00146", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00147", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00148", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00149", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00150", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "shared_dob", - "item": "ref-00151", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "common_surname", - "item": "ref-00152", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00153", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00154", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "planted_hyphenated", - "item": "plant-h-0", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00155", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00156", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "dup_resend", - "item": "ref-00157", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00158", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "shared_dob", - "item": "ref-00159", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-10", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00160", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00161", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "common_surname", - "item": "ref-00162", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00163", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "pl_married_moved", - "item": "pl-mm-3", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00164", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00165", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00166", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00167", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00168", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "r2_name_swap", - "item": "r2-sw-2", - "record": "close", - "record_why": [ - "first and last name swapped" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "ambiguous_same_person", - "item": "ref-00169", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00170", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "dup_existing", - "item": "ref-00171", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00172", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "dup_resend", - "item": "ref-00173", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "rv_last_typo_dob_typo", - "item": "rv-lt-2", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "common_surname", - "item": "ref-00174", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00175", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00176", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "common_surname", - "item": "ref-00177", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "malformed_dob", - "item": "ref-00178", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00179", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00180", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00181", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "ambiguous_new_person", - "item": "ref-00182", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "shared_dob", - "item": "ref-00183", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00184", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-6", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "ambiguous_new_person", - "item": "ref-00185", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "dup_resend", - "item": "ref-00186", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00187", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-2", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "session_expiry", - "item": "ref-00188", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "pl_initial", - "item": "pl-in-8", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-8", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00189", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00190", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00191", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-8", - "record": "close", - "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00192", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "ambiguous_same_person", - "item": "ref-00193", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "dup_resend", - "item": "ref-00194", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "common_surname", - "item": "ref-00195", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00196", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00197", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00198", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "common_surname", - "item": "ref-00199", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00200", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "concurrent_other", - "item": "ref-00201", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00202", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "failed_save", - "item": "ref-00203", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00204", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00205", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00206", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00207", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00208", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "missing_optional", - "item": "ref-00209", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00210", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "shared_dob", - "item": "ref-00211", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "planted_hyphenated", - "item": "plant-h-7", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00212", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00213", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-1", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "planted_hyphenated", - "item": "plant-h-9", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00214", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "pl_initial", - "item": "pl-in-0", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00215", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00216", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "r2_year_transposed", - "item": "r2-ty-1", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "common_surname", - "item": "ref-00217", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00218", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "session_expiry", - "item": "ref-00219", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "common_surname", - "item": "ref-00220", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00221", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "pl_initial", - "item": "pl-in-3", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00222", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00223", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00224", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00225", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00226", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "shared_dob", - "item": "ref-00227", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "rv_last_typo_dob_typo", - "item": "rv-lt-3", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "heldout_first_typo", - "item": "held-f-9", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "dup_existing", - "item": "ref-00228", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth", - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "shared_dob", - "item": "ref-00229", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00230", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00231", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00232", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "concurrent_same", - "item": "ref-00233", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00234", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "planted_married", - "item": "plant-m-8", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-7", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "malformed_case", - "item": "ref-00235", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "missing_optional", - "item": "ref-00236", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00237", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "pl_initial", - "item": "pl-in-5", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "planted_hyphenated", - "item": "plant-h-1", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "failed_save", - "item": "ref-00238", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "session_expiry", - "item": "ref-00239", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "malformed_dob", - "item": "ref-00240", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "slow_save", - "item": "ref-00241", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "malformed_case", - "item": "ref-00242", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00243", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "concurrent_same", - "item": "ref-00244", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "slow_save", - "item": "ref-00245", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "malformed_dob", - "item": "ref-00246", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-6", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "concurrent_other", - "item": "ref-00247", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "missing_optional", - "item": "ref-00248", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "heldout_first_typo", - "item": "held-f-6", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "shared_dob", - "item": "ref-00249", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00250", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "shared_dob", - "item": "ref-00251", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00252", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00253", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "common_surname", - "item": "ref-00254", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00255", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "missing_required", - "item": "ref-00256", - "record": "unreadable", - "record_why": [], - "right": "no chart", - "screen": "stop" - }, - { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-9", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00257", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00258", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "malformed_case", - "item": "ref-00259", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "concurrent_other", - "item": "ref-00260", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00261", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00262", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "planted_swapped_dob", - "item": "plant-s-4", - "record": "close", - "record_why": [ - "same name, day and month swapped" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00263", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "malformed_case", - "item": "ref-00264", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00265", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00266", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "missing_optional", - "item": "ref-00267", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "heldout_last_typo", - "item": "held-l-11", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00268", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "missing_optional", - "item": "ref-00269", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00270", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00271", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00272", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00273", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-5", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "ambiguous_new_person", - "item": "ref-00274", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "ambiguous_new_person", - "item": "ref-00275", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-3", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00276", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00277", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-1", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_year_transposed", - "item": "r2-ty-3", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00278", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00279", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "planted_hyphenated", - "item": "plant-h-5", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "missing_optional", - "item": "ref-00280", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-11", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00281", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00282", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "missing_required", - "item": "ref-00283", - "record": "unreadable", - "record_why": [], - "right": "no chart", - "screen": "stop" - }, - { - "exception": "slow_save", - "item": "ref-00284", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "common_surname", - "item": "ref-00285", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "missing_required", - "item": "ref-00286", - "record": "unreadable", - "record_why": [], - "right": "no chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00287", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00288", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00289", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00290", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "common_surname", - "item": "ref-00291", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00292", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "heldout_first_typo", - "item": "held-f-3", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "dup_resend", - "item": "ref-00293", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-2", - "record": "close", - "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00294", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00295", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "malformed_dob", - "item": "ref-00296", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "ambiguous_same_person", - "item": "ref-00297", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "shared_dob", - "item": "ref-00298", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "dup_existing", - "item": "ref-00299", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth", - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00300", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "rv_last_typo_dob_typo", - "item": "rv-lt-9", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "ambiguous_same_person", - "item": "ref-00301", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "common_surname", - "item": "ref-00302", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "heldout_first_typo", - "item": "held-f-8", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00303", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-6", - "record": "close", - "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "heldout_first_typo", - "item": "held-f-5", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00304", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00305", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00306", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00307", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "session_expiry", - "item": "ref-00308", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "rv_last_typo_dob_typo", - "item": "rv-lt-6", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00309", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "rv_last_typo_dob_typo", - "item": "rv-lt-4", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "concurrent_other", - "item": "ref-00310", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00311", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "heldout_last_typo", - "item": "held-l-5", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00312", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "concurrent_other", - "item": "ref-00313", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "heldout_first_typo", - "item": "held-f-10", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "failed_save", - "item": "ref-00314", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "r2_name_swap", - "item": "r2-sw-10", - "record": "close", - "record_why": [ - "first and last name swapped" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "planted_married", - "item": "plant-m-4", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00315", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00316", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "missing_optional", - "item": "ref-00317", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "heldout_last_typo", - "item": "held-l-10", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_initial", - "item": "pl-in-10", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-10", - "record": "close", - "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "heldout_last_typo", - "item": "held-l-8", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "shared_dob", - "item": "ref-00318", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00319", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00320", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "shared_dob", - "item": "ref-00321", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "failed_save", - "item": "ref-00322", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "dup_resend", - "item": "ref-00323", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00324", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00325", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00326", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00327", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00328", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "session_expiry", - "item": "ref-00329", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "malformed_case", - "item": "ref-00330", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "malformed_dob", - "item": "ref-00331", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "planted_married", - "item": "plant-m-0", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "ambiguous_new_person", - "item": "ref-00332", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "missing_optional", - "item": "ref-00333", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "heldout_last_typo", - "item": "held-l-7", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00334", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "slow_save", - "item": "ref-00335", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00336", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "shared_dob", - "item": "ref-00337", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "shared_dob", - "item": "ref-00338", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "heldout_last_typo", - "item": "held-l-6", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_name_swap", - "item": "r2-sw-3", - "record": "close", - "record_why": [ - "first and last name swapped" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_first_typo", - "item": "held-f-7", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_name_swap", - "item": "r2-sw-9", - "record": "close", - "record_why": [ - "first and last name swapped" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_married_moved", - "item": "pl-mm-7", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00339", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "concurrent_other", - "item": "ref-00340", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00341", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00342", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00343", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "failed_save", - "item": "ref-00344", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "planted_married", - "item": "plant-m-11", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "common_surname", - "item": "ref-00345", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00346", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00347", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00348", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "planted_hyphenated", - "item": "plant-h-8", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "missing_optional", - "item": "ref-00349", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00350", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "ambiguous_new_person", - "item": "ref-00351", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00352", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00353", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "malformed_dob", - "item": "ref-00354", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "pl_married_moved", - "item": "pl-mm-2", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00355", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "shared_dob", - "item": "ref-00356", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "common_surname", - "item": "ref-00357", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00358", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "missing_required", - "item": "ref-00359", - "record": "unreadable", - "record_why": [], - "right": "no chart", - "screen": "stop" - }, - { - "exception": "dup_resend", - "item": "ref-00360", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00361", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "malformed_dob", - "item": "ref-00362", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "slow_save", - "item": "ref-00363", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-7", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "concurrent_other", - "item": "ref-00364", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00365", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00366", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00367", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "planted_hyphenated", - "item": "plant-h-10", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00368", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00369", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "r2_year_transposed", - "item": "r2-ty-0", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00370", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00371", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00372", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00373", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00374", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00375", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00376", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00377", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00378", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00379", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "shared_dob", - "item": "ref-00380", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00381", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00382", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00383", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-4", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "rv_last_typo_dob_typo", - "item": "rv-lt-7", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "r2_name_swap", - "item": "r2-sw-4", - "record": "close", - "record_why": [ - "first and last name swapped" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "rv_last_typo_dob_typo", - "item": "rv-lt-10", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "common_surname", - "item": "ref-00384", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00385", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "malformed_dob", - "item": "ref-00386", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00387", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00388", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00389", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "pl_married_moved", - "item": "pl-mm-1", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00390", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "missing_required", - "item": "ref-00391", - "record": "unreadable", - "record_why": [], - "right": "no chart", - "screen": "stop" - }, - { - "exception": "concurrent_other", - "item": "ref-00392", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00393", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "missing_optional", - "item": "ref-00394", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "planted_hyphenated", - "item": "plant-h-6", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00395", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "malformed_case", - "item": "ref-00396", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00397", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "shared_dob", - "item": "ref-00398", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00399", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "missing_optional", - "item": "ref-00400", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-8", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "common_surname", - "item": "ref-00401", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "dup_existing", - "item": "ref-00402", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth", - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_first_typo", - "item": "held-f-0", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "planted_swapped_dob", - "item": "plant-s-3", - "record": "close", - "record_why": [ - "same name, day and month swapped" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "missing_required", - "item": "ref-00403", - "record": "unreadable", - "record_why": [], - "right": "no chart", - "screen": "stop" - }, - { - "exception": "r2_year_transposed", - "item": "r2-ty-6", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00404", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "ambiguous_same_person", - "item": "ref-00405", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "planted_married", - "item": "plant-m-2", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00406", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00407", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00408", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "r2_year_transposed", - "item": "r2-ty-5", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00409", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00410", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "missing_required", - "item": "ref-00411", - "record": "unreadable", - "record_why": [], - "right": "no chart", - "screen": "stop" - }, - { - "exception": "missing_required", - "item": "ref-00412", - "record": "unreadable", - "record_why": [], - "right": "no chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00413", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "planted_hyphenated", - "item": "plant-h-4", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00414", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00415", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-9", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-11", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00416", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "concurrent_other", - "item": "ref-00417", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-0", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00418", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00419", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "heldout_last_typo", - "item": "held-l-1", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-5", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00420", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "dup_resend", - "item": "ref-00421", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00422", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-7", - "record": "close", - "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00423", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "dup_resend", - "item": "ref-00424", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-3", - "record": "close", - "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00425", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-6", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "ambiguous_new_person", - "item": "ref-00426", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00427", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00428", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00429", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "dup_existing", - "item": "ref-00430", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth", - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "dup_existing", - "item": "ref-00431", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth", - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00432", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "r2_year_transposed", - "item": "r2-ty-9", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "shared_dob", - "item": "ref-00433", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-0", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00434", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00435", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "heldout_last_typo", - "item": "held-l-3", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00436", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "ambiguous_same_person", - "item": "ref-00437", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00438", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "pl_initial", - "item": "pl-in-11", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00439", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00440", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00441", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "pl_initial", - "item": "pl-in-4", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00442", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-5", - "record": "close", - "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "shared_dob", - "item": "ref-00443", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "missing_required", - "item": "ref-00444", - "record": "unreadable", - "record_why": [], - "right": "no chart", - "screen": "stop" - }, - { - "exception": "slow_save", - "item": "ref-00445", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00446", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "planted_swapped_dob", - "item": "plant-s-10", - "record": "close", - "record_why": [ - "same name, day and month swapped" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00447", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00448", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "dup_resend", - "item": "ref-00449", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00450", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00451", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00452", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00453", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "session_expiry", - "item": "ref-00454", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "shared_dob", - "item": "ref-00455", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00456", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-3", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00457", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "pl_initial", - "item": "pl-in-9", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "malformed_case", - "item": "ref-00458", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "slow_save", - "item": "ref-00459", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00460", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "failed_save", - "item": "ref-00461", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "common_surname", - "item": "ref-00462", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-7", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00463", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00464", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "pl_initial", - "item": "pl-in-7", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00465", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00466", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00467", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "dup_resend", - "item": "ref-00468", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-4", - "record": "close", - "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-11", - "record": "close", - "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00469", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "planted_married", - "item": "plant-m-3", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "missing_required", - "item": "ref-00470", - "record": "unreadable", - "record_why": [], - "right": "no chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00471", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00472", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "slow_save", - "item": "ref-00473", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00474", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00475", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "dup_resend", - "item": "ref-00476", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00477", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "ambiguous_same_person", - "item": "ref-00478", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "concurrent_same", - "item": "ref-00479", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "dup_resend", - "item": "ref-00480", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00481", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "common_surname", - "item": "ref-00482", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00483", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "common_surname", - "item": "ref-00484", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00485", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-8", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00486", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-3", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00487", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "common_surname", - "item": "ref-00488", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "shared_dob", - "item": "ref-00489", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00490", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00491", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "failed_save", - "item": "ref-00492", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00493", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00494", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00495", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00496", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "pl_married_moved", - "item": "pl-mm-9", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00497", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00498", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00499", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-9", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00500", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "r2_nickname", - "item": "r2-nk-0", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "rv_nickname_dob_typo", - "item": "rv-nd-0", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_nickname", - "item": "r2-nk-1", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "rv_nickname_dob_typo", - "item": "rv-nd-1", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-4", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "planted_hyphenated", - "item": "plant-h-3", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_married_moved", - "item": "pl-mm-10", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_nickname", - "item": "r2-nk-2", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "rv_nickname_dob_typo", - "item": "rv-nd-2", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_nickname", - "item": "r2-nk-3", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "rv_nickname_dob_typo", - "item": "rv-nd-3", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_nickname", - "item": "r2-nk-4", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "rv_nickname_dob_typo", - "item": "rv-nd-4", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "planted_married", - "item": "plant-m-6", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_nickname", - "item": "r2-nk-5", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_married_moved", - "item": "pl-mm-0", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "rv_nickname_dob_typo", - "item": "rv-nd-5", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_second_surname", - "item": "pl-ss-0", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_second_surname", - "item": "pl-ss-1", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_second_surname", - "item": "pl-ss-2", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_second_surname", - "item": "pl-ss-3", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "planted_married", - "item": "plant-m-5", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_second_surname", - "item": "pl-ss-4", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_second_surname", - "item": "pl-ss-5", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_name_swap", - "item": "r2-sw-5", - "record": "close", - "record_why": [ - "first and last name swapped" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_prior_name_moved_dob_typo", - "item": "r2-pn-0", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth (a prior name on the chart)" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "r2_prior_name_moved_dob_typo", - "item": "r2-pn-1", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth (a prior name on the chart)" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "r2_prior_name_moved_dob_typo", - "item": "r2-pn-2", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth (a prior name on the chart)" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "r2_prior_name_moved_dob_typo", - "item": "r2-pn-3", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth (a prior name on the chart)" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "r2_prior_name_moved_dob_typo", - "item": "r2-pn-4", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth (a prior name on the chart)" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-2", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_first_typo", - "item": "held-f-2", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_prior_name_moved_dob_typo", - "item": "r2-pn-5", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth (a prior name on the chart)" - ], - "right": "no chart: duplicate", - "screen": "proceed" - } - ], - "summary": { - "items": 694, - "new_chart_items": 440, - "record": { - "caught_duplicates": 217, - "decisions": { - "close": 182, - "exact": 37, - "none": 462, - "unreadable": 13 - }, - "false_stops": 2, - "false_stops_by_exception": { - "shared_dob": 2 - }, - "false_stops_per_100_items": 0.3, - "missed_by_exception": { - "limit_married_moved_dob_typo": 12, - "limit_new_name_moved_same_dob": 12 - }, - "missed_duplicates": 24 - }, - "screen": { - "caught_duplicates": 199, - "false_stops": 252, - "false_stops_by_exception": { - "ambiguous_new_person": 7, - "clean": 151, - "common_surname": 27, - "concurrent_other": 7, - "failed_save": 3, - "malformed_case": 6, - "malformed_dob": 2, - "missing_optional": 7, - "session_expiry": 4, - "shared_dob": 33, - "slow_save": 5 - }, - "false_stops_per_100_items": 36.3, - "missed_by_exception": { - "heldout_married_dob_typo": 12, - "limit_married_moved_dob_typo": 12, - "r2_prior_name_moved_dob_typo": 6, - "rv_last_typo_dob_typo": 12 - }, - "missed_duplicates": 42 - }, - "true_duplicates": 241 - } -} diff --git a/packages/flow/benchmark/dup_check/results/soak-seed7-55.json b/packages/flow/benchmark/dup_check/results/soak-seed7-55.json deleted file mode 100644 index 4191bb65f..000000000 --- a/packages/flow/benchmark/dup_check/results/soak-seed7-55.json +++ /dev/null @@ -1,2436 +0,0 @@ -{ - "rows": [ - { - "exception": "clean", - "item": "ref-00001", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "r2_name_swap", - "item": "r2-sw-11", - "record": "close", - "record_why": [ - "first and last name swapped" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00002", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00003", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-10", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00004", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-0", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "planted_married", - "item": "plant-m-8", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_year_transposed", - "item": "r2-ty-2", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "shared_dob", - "item": "ref-00005", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-2", - "record": "close", - "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "malformed_dob", - "item": "ref-00006", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "planted_married", - "item": "plant-m-2", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-5", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "rv_last_typo_dob_typo", - "item": "rv-lt-1", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "heldout_last_typo", - "item": "held-l-0", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_first_typo", - "item": "held-f-1", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "rv_last_typo_dob_typo", - "item": "rv-lt-8", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "pl_initial", - "item": "pl-in-0", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_first_typo", - "item": "held-f-7", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00007", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "planted_hyphenated", - "item": "plant-h-7", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_last_typo", - "item": "held-l-9", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_year_transposed", - "item": "r2-ty-10", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-3", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_initial", - "item": "pl-in-6", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "planted_married", - "item": "plant-m-3", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-3", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-5", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_last_typo", - "item": "held-l-5", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-1", - "record": "close", - "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-6", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "missing_required", - "item": "ref-00008", - "record": "unreadable", - "record_why": [], - "right": "no chart", - "screen": "stop" - }, - { - "exception": "pl_initial", - "item": "pl-in-4", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_name_swap", - "item": "r2-sw-8", - "record": "close", - "record_why": [ - "first and last name swapped" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_initial", - "item": "pl-in-2", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-2", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "rv_last_typo_dob_typo", - "item": "rv-lt-11", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-8", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_initial", - "item": "pl-in-9", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-0", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_married_moved", - "item": "pl-mm-10", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_initial", - "item": "pl-in-1", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_name_swap", - "item": "r2-sw-10", - "record": "close", - "record_why": [ - "first and last name swapped" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-0", - "record": "close", - "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00009", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "pl_initial", - "item": "pl-in-3", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00010", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "heldout_first_typo", - "item": "held-f-11", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-5", - "record": "close", - "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "rv_last_typo_dob_typo", - "item": "rv-lt-10", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-9", - "record": "close", - "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "r2_year_transposed", - "item": "r2-ty-0", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_year_transposed", - "item": "r2-ty-3", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00011", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "concurrent_same", - "item": "ref-00012", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "concurrent_other", - "item": "ref-00013", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "heldout_last_typo", - "item": "held-l-8", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00014", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00015", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "pl_initial", - "item": "pl-in-5", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00016", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "r2_name_swap", - "item": "r2-sw-2", - "record": "close", - "record_why": [ - "first and last name swapped" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-10", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_initial", - "item": "pl-in-10", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00017", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00018", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "rv_last_typo_dob_typo", - "item": "rv-lt-5", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-1", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_last_typo", - "item": "held-l-11", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-10", - "record": "close", - "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "pl_married_moved", - "item": "pl-mm-8", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "malformed_dob", - "item": "ref-00019", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "planted_hyphenated", - "item": "plant-h-0", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-11", - "record": "close", - "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00020", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "planted_swapped_dob", - "item": "plant-s-3", - "record": "close", - "record_why": [ - "same name, day and month swapped" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-2", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "dup_resend", - "item": "ref-00021", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-8", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "rv_last_typo_dob_typo", - "item": "rv-lt-4", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-11", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00022", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "heldout_last_typo", - "item": "held-l-1", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-0", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-9", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00023", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "pl_married_moved", - "item": "pl-mm-9", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_name_swap", - "item": "r2-sw-3", - "record": "close", - "record_why": [ - "first and last name swapped" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "rv_last_typo_dob_typo", - "item": "rv-lt-3", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-3", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "ambiguous_same_person", - "item": "ref-00024", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00025", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "malformed_case", - "item": "ref-00026", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-8", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-11", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "common_surname", - "item": "ref-00027", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00028", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "concurrent_same", - "item": "ref-00029", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-6", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_initial", - "item": "pl-in-7", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "common_surname", - "item": "ref-00030", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00031", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "pl_married_moved", - "item": "pl-mm-4", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "missing_optional", - "item": "ref-00032", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "common_surname", - "item": "ref-00033", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "planted_swapped_dob", - "item": "plant-s-10", - "record": "close", - "record_why": [ - "same name, day and month swapped" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_year_transposed", - "item": "r2-ty-4", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-2", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_married_moved", - "item": "pl-mm-11", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-7", - "record": "close", - "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "dup_existing", - "item": "ref-00034", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00035", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "heldout_last_typo", - "item": "held-l-10", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_last_typo", - "item": "held-l-2", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00036", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00037", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "heldout_first_typo", - "item": "held-f-5", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_first_typo", - "item": "held-f-6", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-7", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_first_typo", - "item": "held-f-8", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "slow_save", - "item": "ref-00038", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00039", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "r2_year_transposed", - "item": "r2-ty-9", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "planted_hyphenated", - "item": "plant-h-10", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "planted_hyphenated", - "item": "plant-h-9", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "shared_dob", - "item": "ref-00040", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "planted_married", - "item": "plant-m-9", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-4", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "shared_dob", - "item": "ref-00041", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-9", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "planted_married", - "item": "plant-m-0", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-10", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_last_typo", - "item": "held-l-6", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "rv_last_typo_dob_typo", - "item": "rv-lt-0", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-6", - "record": "close", - "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "r2_year_transposed", - "item": "r2-ty-1", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-7", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_initial", - "item": "pl-in-11", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_name_swap", - "item": "r2-sw-4", - "record": "close", - "record_why": [ - "first and last name swapped" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_name_swap", - "item": "r2-sw-9", - "record": "close", - "record_why": [ - "first and last name swapped" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00042", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "pl_married_moved", - "item": "pl-mm-5", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00043", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "heldout_first_typo", - "item": "held-f-3", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-11", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-2", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "planted_swapped_dob", - "item": "plant-s-4", - "record": "close", - "record_why": [ - "same name, day and month swapped" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "planted_married", - "item": "plant-m-5", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "planted_hyphenated", - "item": "plant-h-1", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "dup_resend", - "item": "ref-00044", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "planted_hyphenated", - "item": "plant-h-3", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "dup_existing", - "item": "ref-00045", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth", - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "planted_married", - "item": "plant-m-1", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "session_expiry", - "item": "ref-00046", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "clean", - "item": "ref-00047", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "rv_last_typo_dob_typo", - "item": "rv-lt-2", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "heldout_first_typo", - "item": "held-f-9", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-11", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00048", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "planted_hyphenated", - "item": "plant-h-8", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "planted_hyphenated", - "item": "plant-h-11", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_married_moved", - "item": "pl-mm-7", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_first_typo", - "item": "held-f-0", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00049", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-9", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-4", - "record": "close", - "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "r2_year_transposed", - "item": "r2-ty-8", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00050", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "rv_last_typo_dob_typo", - "item": "rv-lt-9", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-5", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "clean", - "item": "ref-00051", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "dup_existing", - "item": "ref-00052", - "record": "exact", - "record_why": [ - "same first name, last name and date of birth", - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-3", - "record": "close", - "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "pl_married_moved", - "item": "pl-mm-6", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_year_transposed", - "item": "r2-ty-5", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-6", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "planted_hyphenated", - "item": "plant-h-2", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_first_typo", - "item": "held-f-2", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "missing_optional", - "item": "ref-00053", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "proceed" - }, - { - "exception": "common_surname", - "item": "ref-00054", - "record": "none", - "record_why": [], - "right": "new chart", - "screen": "stop" - }, - { - "exception": "pl_married_moved", - "item": "pl-mm-3", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-1", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-10", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_married_moved", - "item": "pl-mm-1", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_name_swap", - "item": "r2-sw-1", - "record": "close", - "record_why": [ - "first and last name swapped" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "missing_required", - "item": "ref-00055", - "record": "unreadable", - "record_why": [], - "right": "no chart", - "screen": "stop" - }, - { - "exception": "r2_nickname", - "item": "r2-nk-0", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_year_transposed", - "item": "r2-ty-6", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "planted_hyphenated", - "item": "plant-h-5", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-0", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "rv_nickname_dob_typo", - "item": "rv-nd-0", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-1", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-5", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_ambiguous_same_person", - "item": "pl-as-1", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "planted_hyphenated", - "item": "plant-h-4", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_nickname", - "item": "r2-nk-1", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_first_typo", - "item": "held-f-10", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "rv_nickname_dob_typo", - "item": "rv-nd-1", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_nickname", - "item": "r2-nk-2", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "rv_nickname_dob_typo", - "item": "rv-nd-2", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_year_transposed", - "item": "r2-ty-7", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_married_dob_typo", - "item": "held-md-8", - "record": "close", - "record_why": [ - "same first name, another last name, same phone, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "heldout_last_typo", - "item": "held-l-3", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_last_typo", - "item": "held-l-4", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_nickname", - "item": "r2-nk-3", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "planted_married", - "item": "plant-m-10", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "rv_nickname_dob_typo", - "item": "rv-nd-3", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_name_swap", - "item": "r2-sw-5", - "record": "close", - "record_why": [ - "first and last name swapped" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-4", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_name_swap", - "item": "r2-sw-0", - "record": "close", - "record_why": [ - "first and last name swapped" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "planted_married", - "item": "plant-m-6", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "heldout_first_typo", - "item": "held-f-4", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "rv_last_typo_dob_typo", - "item": "rv-lt-6", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "r2_nickname", - "item": "r2-nk-4", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "rv_nickname_dob_typo", - "item": "rv-nd-4", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "planted_hyphenated", - "item": "plant-h-6", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_nickname", - "item": "r2-nk-5", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_married_moved", - "item": "pl-mm-0", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_name_swap", - "item": "r2-sw-6", - "record": "close", - "record_why": [ - "first and last name swapped" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "rv_nickname_dob_typo", - "item": "rv-nd-5", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_second_surname", - "item": "pl-ss-0", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_name_swap", - "item": "r2-sw-7", - "record": "close", - "record_why": [ - "first and last name swapped" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-4", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "planted_married", - "item": "plant-m-11", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-7", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "pl_second_surname", - "item": "pl-ss-1", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_second_surname", - "item": "pl-ss-2", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_second_surname", - "item": "pl-ss-3", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_second_surname", - "item": "pl-ss-4", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-3", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "limit_married_moved_dob_typo", - "item": "lim-mm-8", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "pl_second_surname", - "item": "pl-ss-5", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "rv_last_typo_dob_typo", - "item": "rv-lt-7", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "pl_married_moved", - "item": "pl-mm-2", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "planted_married", - "item": "plant-m-7", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_prior_name_moved_dob_typo", - "item": "r2-pn-0", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth (a prior name on the chart)" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "r2_prior_name_moved_dob_typo", - "item": "r2-pn-1", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth (a prior name on the chart)" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "r2_prior_name_moved_dob_typo", - "item": "r2-pn-2", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth (a prior name on the chart)" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "r2_prior_name_moved_dob_typo", - "item": "r2-pn-3", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth (a prior name on the chart)" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-9", - "record": "close", - "record_why": [ - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-7", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_initial", - "item": "pl-in-8", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "r2_prior_name_moved_dob_typo", - "item": "r2-pn-4", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth (a prior name on the chart)" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "r2_prior_name_moved_dob_typo", - "item": "r2-pn-5", - "record": "close", - "record_why": [ - "same or similar name, a mistyped date of birth (a prior name on the chart)" - ], - "right": "no chart: duplicate", - "screen": "proceed" - }, - { - "exception": "heldout_last_typo", - "item": "held-l-7", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "planted_married", - "item": "plant-m-4", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth and first name, another last name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "pl_both_typos_same_contact", - "item": "pl-bt-4", - "record": "close", - "record_why": [ - "same date of birth and first name, another last name", - "same date of birth, a similar name" - ], - "right": "no chart: duplicate", - "screen": "stop" - }, - { - "exception": "limit_new_name_moved_same_dob", - "item": "lim-nn-6", - "record": "none", - "record_why": [], - "right": "no chart: duplicate", - "screen": "stop" - } - ], - "summary": { - "items": 249, - "new_chart_items": 45, - "record": { - "caught_duplicates": 178, - "decisions": { - "close": 171, - "exact": 7, - "none": 69, - "unreadable": 2 - }, - "false_stops": 0, - "false_stops_by_exception": {}, - "false_stops_per_100_items": 0.0, - "missed_by_exception": { - "limit_married_moved_dob_typo": 12, - "limit_new_name_moved_same_dob": 12 - }, - "missed_duplicates": 24 - }, - "screen": { - "caught_duplicates": 160, - "false_stops": 10, - "false_stops_by_exception": { - "clean": 2, - "common_surname": 4, - "shared_dob": 3, - "slow_save": 1 - }, - "false_stops_per_100_items": 4.0, - "missed_by_exception": { - "heldout_married_dob_typo": 12, - "limit_married_moved_dob_typo": 12, - "r2_prior_name_moved_dob_typo": 6, - "rv_last_typo_dob_typo": 12 - }, - "missed_duplicates": 42 - }, - "true_duplicates": 202 - } -} diff --git a/packages/flow/public-artifacts.json b/packages/flow/public-artifacts.json index 385296d66..fb115f7be 100644 --- a/packages/flow/public-artifacts.json +++ b/packages/flow/public-artifacts.json @@ -133,8 +133,6 @@ "benchmark/dom/finals/perturbation_dom_named_028.png", "benchmark/dom/outcome_matrix.png", "benchmark/dom/results.json", - "benchmark/dup_check/results/seed7-500.json", - "benchmark/dup_check/results/soak-seed7-55.json", "benchmark/effect_e2e/effect_e2e.png", "benchmark/effect_e2e/results.json", "benchmark/effect_readback/results.json", From aee3ad78b3e879b0755f12bdfef6eed5c9fde693 Mon Sep 17 00:00:00 2001 From: abrichr Date: Sat, 24 Oct 2026 23:26:01 -0400 Subject: [PATCH 12/13] duplicate check: live FHIR rates harness (runs on the fixture lease) Co-Authored-By: Claude Opus 5.5 --- .../flow/benchmark/dup_check/live_rates.py | 163 ++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 packages/flow/benchmark/dup_check/live_rates.py diff --git a/packages/flow/benchmark/dup_check/live_rates.py b/packages/flow/benchmark/dup_check/live_rates.py new file mode 100644 index 000000000..817210839 --- /dev/null +++ b/packages/flow/benchmark/dup_check/live_rates.py @@ -0,0 +1,163 @@ +"""False stops and missed duplicates through live OpenEMR FHIR, on the soak's stream. + + python -m benchmark.dup_check.live_rates --out DIR [--long-items 500] + +The offline measurement (``measure.py``) reads an in-memory table with FHIR +search semantics. This one asks a real OpenEMR 8.0.0.3: it resets the pinned +fixture, creates the existing charts through OpenEMR's REST API (and stores +each planted chart's birth name the way OpenEMR's demographics do), then for +every item in the planted seed-7 stream runs ``check_duplicate`` through +``FhirPatientSource`` and, when the right outcome is a new chart, creates it. +So the record check meets the same charts as offline, served by OpenEMR. + +Needs the fixture lease and ``OPENEMR_FIXTURE_STATE`` / ``OPENEMR_UI_PORT`` / +``OPENEMR_API_PORT``. No model calls. Writes ``rows.json`` (per item, private +evidence) and ``summary.json`` to ``--out``. +""" + +from __future__ import annotations + +import argparse +import json +import sys +import time +from pathlib import Path +from typing import Any + +HERE = Path(__file__).resolve().parent +sys.path.insert(0, str(HERE.parent)) + +from benchmark.dup_check import measure # noqa: E402 +from openadapt_flow.runtime.checks.duplicate_record import ( # noqa: E402 + FhirPatientSource, + check_duplicate, +) + +SCOPE = "openid api:oemr api:fhir user/patient.crus user/Patient.read user/Patient.rs" +API_FIELDS = ( + "title", + "fname", + "lname", + "DOB", + "sex", + "street", + "city", + "postal_code", + "phone_home", + "email", + "country_code", +) + + +def main() -> int: + from verification_coverage.independent import live # the fixture helpers + + parser = argparse.ArgumentParser(description=__doc__.splitlines()[0]) + parser.add_argument("--out", type=Path, required=True) + parser.add_argument("--long-items", type=int, default=500) + parser.add_argument("--budget-s", type=float, default=3000.0) + args = parser.parse_args() + args.out.mkdir(parents=True, exist_ok=True) + started = time.monotonic() + + import httpx + + fx = live.fx() + live.reset() + clients = json.loads(fx.clients_path.read_text()) + live.sql( + f"UPDATE openemr.oauth_clients SET scope = {live.q(SCOPE)} " + f"WHERE client_id = {live.q(clients['actor_client_id'])};" + ) + values = fx._runtime_values() + session = httpx.Client(verify=False, timeout=30) + tokens: dict[str, Any] = {} + + def token() -> str: + # Tokens live an hour; take a fresh one every 30 minutes. + if not tokens or time.monotonic() - tokens["at"] > 1800: + r = session.post( + f"{fx.api_base_url}/oauth2/default/token", + data={ + "grant_type": "password", + "client_id": clients["actor_client_id"], + "scope": SCOPE, + "user_role": "users", + "username": values["OPENEMR_ACTOR_USER"], + "password": values["OPENEMR_ACTOR_PASSWORD"], + }, + ) + r.raise_for_status() + tokens.update(value=r.json()["access_token"], at=time.monotonic()) + return str(tokens["value"]) + + def create(person: dict[str, str]) -> None: + body = {k: str(person.get(k, "") or "") for k in API_FIELDS} + body["DOB"] = measure.iso(body["DOB"]) + body["state"] = "MA" + r = session.post( + f"{fx.api_base_url}/apis/default/api/patient", + json=body, + headers={"Authorization": f"Bearer {token()}"}, + ) + if r.status_code not in (200, 201): + raise RuntimeError(f"create: HTTP {r.status_code} {r.text[:200]}") + if person.get("prior_lname"): + live.sql( + "UPDATE openemr.patient_data SET birth_fname = " + f"{live.q(person['fname'])}, birth_lname = {live.q(person['prior_lname'])} " + f"WHERE email = {live.q(person['email'])}" + ) + + existing, items = measure.from_generator(7, args.long_items, 10) + extra, extra_items = measure.extra_existing(existing) + stream = measure.plant(existing, items + extra_items, 7) + for person in existing + extra: + create(person) + source = FhirPatientSource( + f"{fx.api_base_url}/apis/default/fhir", + token=token, + session=httpx.Client(verify=False, timeout=30), + ) + rows = [] + for item in stream: + if time.monotonic() - started > args.budget_s: + raise SystemExit( + f"stopped at the {args.budget_s:.0f} s budget after {len(rows)} items" + ) + for person in item.before: + create(person) + params = { + k: (v.strip() if isinstance(v, str) else v) for k, v in item.params.items() + } + params["DOB"] = measure.iso(params.get("DOB", "")) + result = check_duplicate(source, params) + is_dup = item.exception in measure.DUPLICATE_KINDS + rows.append( + { + "item": item.item_id, + "exception": item.exception, + "right": "new chart" + if item.intended is not None + else ("no chart: duplicate" if is_dup else "no chart"), + "record": result.decision, + "record_why": [c.why for c in result.candidates], + "reason": result.reason if result.decision == "unreadable" else "", + } + ) + if item.intended is not None: + create(item.intended) + summary = measure.summarize([{**r, "screen": "proceed"} for r in rows]) + summary.pop("screen", None) + summary["unreadable_reasons"] = sorted({r["reason"] for r in rows if r["reason"]}) + summary["seconds"] = round(time.monotonic() - started) + (args.out / "rows.json").write_text(json.dumps(rows, indent=1) + "\n") + (args.out / "summary.json").write_text( + json.dumps(summary, indent=2, sort_keys=True) + "\n" + ) + print(json.dumps(summary, sort_keys=True)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 4b635f2d6e1e318c3fdb62c99c777c4ce0037a68 Mon Sep 17 00:00:00 2001 From: abrichr Date: Sat, 24 Oct 2026 23:26:28 -0400 Subject: [PATCH 13/13] CAPABILITIES: no duplicate-check rates until the live FHIR re-run Co-Authored-By: Claude Opus 5.5 --- CAPABILITIES.md | 2 +- llms-full.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CAPABILITIES.md b/CAPABILITIES.md index f122632a7..d911945c7 100644 --- a/CAPABILITIES.md +++ b/CAPABILITIES.md @@ -229,7 +229,7 @@ improvising or retrying when something is off. | Repair by default: once a repair agent is configured, every stop that wrote nothing goes to it without anyone starting it; a stop after a possible write goes to reconciliation; every stop is a row in a ledger with the funnel from stop to resumed item | On main ([#1360](https://github.com/OpenAdaptAI/OpenAdapt/pull/1360)) | Synthetic (MockMed, 22 drift modes, real Claude agent: 6 of 21 repairable stops resumed and verified, 0 wrong saves in 66 runs); routing over recorded OpenEMR stops; live openIMIS relabel: 3 of 3 reached repair, 0 resumed; silent-saves 1.1.0 UI-change blocks: 18 of 28 items that always stopped now correct, 0 wrong | `run` (on by default, `--no-auto-repair`), `repair:` in `deploy.yaml`, [benchmark/auto_repair](packages/flow/benchmark/auto_repair/README.md) | | Repair a relabeled save button: prove from the recorded identity hash that only the label changed (same control id, kind and form), re-qualify the fix on the new release, and have a person admit the certified copy afresh, since a save button's identity is part of the record check | On main | Real app (local OpenEMR, synthetic data): after the save button was relabeled, the stopped item re-ran and saved once after 8.3 operator minutes and 763 s of re-qualification; 896 s from stop to done ([#1513](https://github.com/OpenAdaptAI/OpenAdapt/pull/1513)) | `openadapt_flow/repair/relabel.py`, [SELF_REPAIR.md](packages/flow/docs/SELF_REPAIR.md#a-relabeled-control-proof-that-only-the-label-changed) | | A duplicate check that reads the app's match list (`no_duplicate_candidate`): stop only on a listed record with the item's last name or date of birth, fail closed on an unreadable or unloaded list | On main | Real app (local OpenEMR, synthetic data): false stops per 100 items 70.4 before, 16.4 after; 0 silent wrongs in 30 saves ([#1513](https://github.com/OpenAdaptAI/OpenAdapt/pull/1513)) | [DUPLICATE_CHECK.md](packages/flow/docs/DUPLICATE_CHECK.md) | -| A duplicate check that asks the system of record (generic FHIR R4 `Patient` search; OpenEMR REST and SQL adapters): every page read, stop on an exact or close match, proceed otherwise, fail closed on any read problem. One source for qualification, runs and resume; a program can pin it | On main | Offline, the soak's seed-7 stream with planted true duplicates (694 items, 241 duplicates): false stops per 100 items 36.3 (screen) and 0.3 (record); duplicates missed 42 (screen) and 24 (record). The record's 24 are a name change plus a move, sharing only a first name or only a date of birth with the chart | [DUPLICATE_CHECK.md](packages/flow/docs/DUPLICATE_CHECK.md) | +| A duplicate check that asks the system of record (generic FHIR R4 `Patient` search; OpenEMR REST and SQL adapters): every page read, stop on an exact or close match, proceed otherwise, fail closed on any read problem. One source for qualification, runs and resume; a program can pin it | On main | Synthetic: unit and HTTP tests; live OpenEMR 8.0.0.3 search decided 6 of 6 known referrals right through FHIR | [DUPLICATE_CHECK.md](packages/flow/docs/DUPLICATE_CHECK.md) | | A switched-off `problems_only` gate asks a person before the save instead of stopping the item | On main | Synthetic; real app in [#1513](https://github.com/OpenAdaptAI/OpenAdapt/pull/1513) | [APPROVAL_MODES.md](packages/flow/docs/APPROVAL_MODES.md#when-the-mode-turns-itself-off) | | Fleet drift patches: a verified fix with no patient data, shared across sites under policy | Released, local reference hub | Simulator | `openadapt-flow fleet`, [FLEET_DRIFT_PATCHES.md](packages/flow/docs/FLEET_DRIFT_PATCHES.md) | diff --git a/llms-full.txt b/llms-full.txt index afacfb6ed..548258200 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -626,7 +626,7 @@ improvising or retrying when something is off. | Repair by default: once a repair agent is configured, every stop that wrote nothing goes to it without anyone starting it; a stop after a possible write goes to reconciliation; every stop is a row in a ledger with the funnel from stop to resumed item | On main ([#1360](https://github.com/OpenAdaptAI/OpenAdapt/pull/1360)) | Synthetic (MockMed, 22 drift modes, real Claude agent: 6 of 21 repairable stops resumed and verified, 0 wrong saves in 66 runs); routing over recorded OpenEMR stops; live openIMIS relabel: 3 of 3 reached repair, 0 resumed; silent-saves 1.1.0 UI-change blocks: 18 of 28 items that always stopped now correct, 0 wrong | `run` (on by default, `--no-auto-repair`), `repair:` in `deploy.yaml`, [benchmark/auto_repair](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/benchmark/auto_repair/README.md) | | Repair a relabeled save button: prove from the recorded identity hash that only the label changed (same control id, kind and form), re-qualify the fix on the new release, and have a person admit the certified copy afresh, since a save button's identity is part of the record check | On main | Real app (local OpenEMR, synthetic data): after the save button was relabeled, the stopped item re-ran and saved once after 8.3 operator minutes and 763 s of re-qualification; 896 s from stop to done ([#1513](https://github.com/OpenAdaptAI/OpenAdapt/pull/1513)) | `openadapt_flow/repair/relabel.py`, [SELF_REPAIR.md](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/SELF_REPAIR.md#a-relabeled-control-proof-that-only-the-label-changed) | | A duplicate check that reads the app's match list (`no_duplicate_candidate`): stop only on a listed record with the item's last name or date of birth, fail closed on an unreadable or unloaded list | On main | Real app (local OpenEMR, synthetic data): false stops per 100 items 70.4 before, 16.4 after; 0 silent wrongs in 30 saves ([#1513](https://github.com/OpenAdaptAI/OpenAdapt/pull/1513)) | [DUPLICATE_CHECK.md](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/DUPLICATE_CHECK.md) | -| A duplicate check that asks the system of record (generic FHIR R4 `Patient` search; OpenEMR REST and SQL adapters): every page read, stop on an exact or close match, proceed otherwise, fail closed on any read problem. One source for qualification, runs and resume; a program can pin it | On main | Offline, the soak's seed-7 stream with planted true duplicates (694 items, 241 duplicates): false stops per 100 items 36.3 (screen) and 0.3 (record); duplicates missed 42 (screen) and 24 (record). The record's 24 are a name change plus a move, sharing only a first name or only a date of birth with the chart | [DUPLICATE_CHECK.md](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/DUPLICATE_CHECK.md) | +| A duplicate check that asks the system of record (generic FHIR R4 `Patient` search; OpenEMR REST and SQL adapters): every page read, stop on an exact or close match, proceed otherwise, fail closed on any read problem. One source for qualification, runs and resume; a program can pin it | On main | Synthetic: unit and HTTP tests; live OpenEMR 8.0.0.3 search decided 6 of 6 known referrals right through FHIR | [DUPLICATE_CHECK.md](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/DUPLICATE_CHECK.md) | | A switched-off `problems_only` gate asks a person before the save instead of stopping the item | On main | Synthetic; real app in [#1513](https://github.com/OpenAdaptAI/OpenAdapt/pull/1513) | [APPROVAL_MODES.md](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/APPROVAL_MODES.md#when-the-mode-turns-itself-off) | | Fleet drift patches: a verified fix with no patient data, shared across sites under policy | Released, local reference hub | Simulator | `openadapt-flow fleet`, [FLEET_DRIFT_PATCHES.md](https://github.com/OpenAdaptAI/OpenAdapt/blob/main/packages/flow/docs/FLEET_DRIFT_PATCHES.md) |