Compare two records, or work a review queue¶
arche studio is a small local tool with two jobs: decide whether two records
are the same thing, and walk an adjudication pack marking outcomes.
It is a handful of files and the standard library. No pip install, no framework, no
network access.
It opens http://127.0.0.1:8765 for you.
Compare¶
Paste two records, pick an entity pack, and get the decision with the evidence behind it and a sentence saying why. The fields are free-form, so it works for two bare names or for full records with coordinates and identifiers.
The pair worth trying first is two records both named General Hospital, as
place:
review 1.000
name 1.000
name_tftoken 1.000
name_type 1.000
Held for a human
The fields agree, but nothing that agreed is distinctive. The strongest
signal scored 0.564 against a floor of 0.750.
The score is 1.000 and the engine still will not merge them. General and
Hospital are ordinary words, and agreeing on ordinary words is not evidence.
A matcher that cannot make that distinction merges two hospitals in different
states and reports perfect confidence.
Load an example cycles through four pairs: a Nigerian facility upgraded to a
new tier, two spellings of one Fula surname, a dropped middle name, and two
schools from the same academy chain.
Review queue¶
Point it at an adjudication pack in data/review_packs/ and work through it.
Filter to what needs a human, read both records side by side with the same
evidence panel, mark an outcome, give a reason, save.
Any CSV works. The tool infers which columns belong to which side from their
prefixes, so a pack with grid3_name and hfr_name renders without
configuration.
Export a match result as a pack¶
review_pack turns any crosswalk result into the two files the studio opens.
from arche.report import review_pack
from arche.resolve import crosswalk
register = [{"id": "1", "name": "Amara Patel", "birth_date": "2016-06-28"},
{"id": "2", "name": "Malik Okonkwo", "birth_date": "2017-08-18"}]
survey = [{"id": "1", "name": "Amara Patel", "birth_date": "6/28/2016"},
{"id": "2", "name": "Malik Okonkwo", "birth_date": "2017-08-18"}]
result = crosswalk(register, survey, entity="person", id_field="id")
review_pack(
result, register, survey,
out_dir="data/review_packs/register_x_survey",
sides=("register", "survey"), # column prefixes, and the panel headings
entity="person",
reveal=True, # see below before you copy this
)
That writes:
data/review_packs/register_x_survey/
pack.csv one row per decision, four blank review columns
manifest.json what was run, and a digest of the decision ids
Start the studio and the pack is in the picker:
The CSV columns are decision_id, decision, score, distinctive_max, then
each side under its own prefix, then evidence as JSON, then the four columns a
reviewer fills. distance_km is added when the evidence carries one.
By default only match and review rows are written. A queue of pairs the
engine already rejected is not a queue. Pass
decisions=("match", "review", "no_match") when you want to audit those too.
Masked by default, and why you will usually override it¶
A pack is a file that gets copied around, so it follows the same fail-safe as
crosswalk_report, its HTML sibling: values pass through the masking allowlist,
and record ids that look like national identifiers are refused outright rather
than laundered.
A masked pack is also close to useless for the thing a pack is for. Nobody can
judge whether two people are the same when both names are redacted. So
reveal=True is normal here, and the thing to be deliberate about is where the
file goes afterwards. data/review_packs/ is the intended home, and the
manifest records which of the two you produced:
If your row ids are themselves sensitive, a national ID or a BVN used as the join key, the export refuses in masked mode. Add a surrogate id column rather than revealing to get past it.
The manifest is the reproducibility record¶
{
"schema": "arche.review_pack.v1",
"entity": "person",
"rows": 333,
"decisions": {"match": 267, "review": 66},
"pins": {"comparators_sha256": "8d1e03d23fa6b8a6", "threshold": 0.7, ...},
"decision_ids_sha256": "..."
}
pins says which engine and which comparator set produced the decisions, so a
pack reviewed months later still says what it was. decision_ids_sha256 is what
the studio's integrity digest is checked against.
What it will not let you do¶
Overwrite the matcher's output. Saving writes a new _reviewed.csv beside
the pack. The original and its decision-ID manifest stay intact.
Save anonymously. A reviewer name is required, because an unattributed adjudication cannot be audited.
Review a pack that changed underneath you. The header shows an integrity digest computed over the pack's decision ids. If the file was edited between matching and reviewing, the digest moves.
Scope¶
It binds to 127.0.0.1 and expects to run on the machine holding the data.
There is no authentication because there is no remote access.
It replaces an earlier Streamlit review app, which required a dependency to do less.