NantiFor the judge

The letter now, the ad later: a correctly formatted Indonesian formal letter as a PDF, delivered first, then one rewarded ad on a visible tab that only RevenueCat's server can mark as paid.

Conditional project. The settle round-trip (ad → AdMob server-side verification → RevenueCat → ADS +1) is fully built and in the bundle but has not yet run on a device. docs/SPIKE.md carries RESULT: not yet run. Everything below that depends on it is marked pending.

The 30-second path (no setup, no keys)

  1. Install the app — there is no live Play listing yet (production upload in progress as of 2026-09-26); its link lands here when it is published. Until then the path below runs on a dev build — DEMO.md §2.
  2. Surat Izin Tidak Masuk Kerja → five fields (city and date already filled) → the paper preview updates as you type → Buat PDF → the share sheet opens with a real file name. No ad has appeared.
  3. Back on Home: the amber pill Tab: 1 iklan · bayar nanti.
  4. Tap any tile → the Settle sheet. Tonton 1 iklan → ad → memverifikasi… → ADS 0 → 1, the pill turns green once and disappears (release build only: a dev build always serves Google's test unit, where verification correctly fails and the tab stays). Or Nanti Pro → the RevenueCat paywall → violet Pro chip.
  5. Settings → Bukti RevenueCat: live ADS balance, pro entitlement, app user id, last 5 settles with Δ timings.

Without a phone: npm install --legacy-peer-deps && npx tsx bin/surat.ts demo 1 > letter.html renders the exact HTML the PDF is printed from.

Receipts

ValueVerify
Tests81, < 2 s, no device, no keynpm test
Exhaustive verification70,602 ledger transitions — debt ∈ {0,1}, never lowered by a client event; 299,592 settle-machine paths — settled only via verified; 0 violationstests/exhaustive.test.ts
Self-ablation0 client-side settle increments in src/npm run ablation
Templates8, byte-identical to golden files; p95 ≤ 4 µs per rendernpm run bench
Coverage (pure modules)100 % statements/branches/functions/linesnpm run test:coverage
RevenueCat methods15 across four filesARCHITECTURE.md
Native builddebug APK dev.edycu.nanti — RevenueCat, Google Mobile Ads, SQLite, Print in the dexCI Stage 4
G1 spike (3/3 verified settles, ADS +1 < 60 s)pendingdocs/SPIKE.md
Settle-to-balance latency, N ≥ 20pendingdocs/SETTLE-LOG.md

Reproduce (the real path)

git clone https://github.com/edycutjong/nanti && cd nanti
npm install --legacy-peer-deps
npm test && npm run bench && npm run verify:offline && npm run ablation
cp .env.example .env      # RevenueCat Test Store key · AdMob app id (dev builds always use Google's test ad unit)
npx expo prebuild --platform android && npx expo run:android

No offline / mock / demo flag exists. A dev build always requests Google's test ad unit (AdMob policy; the real unit is release-only). On it the ad plays but verification correctly fails and the tab stays — the app never pretends a grant happened.

Honest limitations

Links

Repository · Play listing (not published yet) · Demo video (not recorded yet) · Landing · Privacy · DEMO.md · docs/SPIKE.md

Mirror of JUDGE.md in the repository · updated 2026-09-26.