#C01 — Technical Reality Check
Reviewer stance: principal engineer, two POS systems shipped, one of which failed — and it failed in month 10 for exactly the reason r04 §7 describes, which is why r04 is the best document of the four.
Scope reviewed in full: r01 (hardware/peripherals), r04 (offline/sync/domain), r05 (backend/platform/AI), r08 (client apps/devices). Cross-read against the digest of r02, r03, r06, r07, r09.
Date: 2026-08-18. Labels: [verified] = checked this session with a source; [known] = confident from training; [estimate] = reasoned guess from having built this.
#Verdict
The technical research is unusually good — better than most funded teams' internal design docs. r04 is close to publishable; r01 §2.8 and §4.5 are written by someone who has actually watched a kitchen printer fail. Three things are wrong badly enough to change decisions.
First, the effort estimates are denominated in four different currencies and then implicitly added. r08 says its 42 eng-months already includes an AI multiplier; r05 applies its multiplier at the bottom of the table (47 → 30–36); r04 applies 0.7× to some line items and 1.0× to others; r01 applies none explicitly. Any roll-up of "24 + 34 + 47 + 42" is meaningless. Worse, r05 item 4 (order domain, 7–9 eng-months) is the same work as r04 items 1+3+5 (10 eng-months), r05 item 6 (realtime/print routing, 3–4) overlaps r01 items 2+5 (5.0) and r04 item 7 (3.5), and r05 item 10 (Z-report/cash reconciliation, 4.5–7) overlaps r08 item 7 (1.0). There are 12–18 eng-months of double-counted or boundary-orphaned work in the roll-up, and nobody can tell which direction the error runs.
Second, the offline core is under-costed by 1.5–1.7× and the two line items that carry the risk (the fold at 4.0, the simulator at 3.5) are the two that are most wrong. Honest full-T3 number is 48–58, not 34.
Third, r05 and r08 give directly contradictory language mandates and nobody noticed. r05 §2.2 makes "one pricing/tax engine, one implementation, runs on device and server" a non-negotiable and recommends pure TypeScript. r08 §2.4 mandates Kotlin for every counter surface. Those cannot both be true. This is a 3–5 eng-month decision and it is unmade.
Everything else in this document is refinement. The strategic conclusions — resell hardware, own the conflict semantics, native at the counter, T0 before T3, KDS over printers — all survive.
#Corrections (claim → correction → confidence)
#C1. "A browser cannot reach an HTTP printer on a private IP — mixed content, no fourth option" (r01 §2.5)
Correction: this is no longer true on Chrome. Chrome shipped a Local Network Access (LNA) permission gating public-origin requests to RFC1918 / loopback / .local, from Chrome 142, and permission-gated local network requests are exempted from mixed-content checks when Chrome knows before DNS resolution that the destination is local. Later releases extend the same permission model to WebSockets, WebTransport and WebRTC. [verified — developer.chrome.com/blog/local-network-access]
What actually changes and what does not:
- An HTTPS PWA on Chrome/Android can now POST ePOS-Print XML to
http://192.168.1.50/cgi-bin/epos/service.cgionce the user grants LNA. r01's "there is no fourth option" is wrong for Chrome. - It changes nothing for iOS/Safari, and nothing for raw TCP 9100 (no browser will ever open a socket). The conclusion — native at the counter — survives on the socket argument alone.
- It introduces a new install-day failure identical to the iOS "Don't Allow", on a platform r01/r08 treated as safe. Worse: Android WebView has no LNA permission UI, so a WebView-hosted surface may be unable to obtain it at all. That is a direct hit on any "back office prints from the browser" feature and on r08's plan to keep back office and QR ordering on web.
- Net: the browser-vs-native decision stays, but the reason in the deck must change, and the LNA prompt joins the iOS multicast/local-network prompt in the install runbook.
[verified]
#C2. "PowerSync Open Edition — unclear whether source-available permits redistribution in a commercial on-prem product" (r04 open question #4)
Correction: resolved, and permissive. PowerSync Open Edition is licensed under the Functional Source License (FSL), which permits copying, modification and redistribution for commercial use, excluding only a paid offering competitive with PowerSync itself. FSL converts to Apache 2.0 two years after release; Open Edition released 2024-05-31, so that conversion has already occurred. [verified — powersync.com/legal/fsl, powersync.com/blog/powersync-open-edition-release]
A POS embedding it as sync transport is not a competing sync product. Downgrade this from an open risk to a closed one. r04's core recommendation (own the semantics, rent the transport) is unaffected and still correct.
#C3. "Hetzner raised cloud prices in April 2026; CCX33 €62.49 → €138.49 [verified, single source]" (r05 §8)
Correction: the date and the count are both wrong. Hetzner raised cloud prices three times in 2026; the CCX (dedicated vCPU) repricing landed 15 June 2026, moving CCX33 from €62.49 → €138.49/mo (+122%), with the CCX/CPX lines up 113–176% depending on tier. Critically: existing untouched servers are grandfathered, but any rescale reprices at the new rate. [verified — northflank.com, wz-it.com, webhosting.today]
Three consequences r05 misses:
- You cannot scale vertically without repricing. Capacity planning must be horizontal from day one — which happens to be what
org_id-leading indexes already enable, so this is cheap, but it must be a stated rule. - The "self-hosted is 3–5× cheaper than managed" claim compresses to roughly 2–2.5×. Still the right call at S1, no longer a slam dunk at S2.
- Provisioning production-shaped instances early locks the old price. Grubby, real, worth ~€2–4k/yr at the 1,000-location shape.
[estimate]
The conclusion "infra is never the constraint, 3–8% of revenue" survives — at the 1,000-location shape the compute lines are ~1.5–2× the doc's figures, taking infra to roughly 8–12% of revenue at €69 ARPU, which is still not the thing that kills you.
#C4. "Anthropic first-party API has no EU inference pin" (r05 §1.3, §7)
No correction — this is right, and so is the model pricing. inference_geo accepts us or global; there is no EU value. Opus 5 $5/$25 per MTok, Sonnet 5 $3/$15 (intro $2/$10 through 2026-08-31), Haiku 4.5 $1/$5, Batch API 50% discount, cache reads ~0.1× / writes 1.25× (5-min TTL) — all as stated. [verified]
One free win the doc missed: the minimum cacheable prefix on Opus 5 is 512 tokens, half of Sonnet 5's 1024. [verified] For the menu-snapshot-as-cached-prefix pattern in §7.8, that means small-venue menus that wouldn't cache on Sonnet do cache on Opus, which partly offsets the model price delta on the NL-analytics path.
#C5. "Business day is stamped by the device at event creation and never derived later" (r04 §4.2, r05 §6.3)
Both docs agree and both are wrong for the failure that matters. r04 correctly refuses to order by wall clock — then stamps the most fiscally consequential field in the system with it. A cheap Android AIO with a dead RTC coin cell (endemic on the EUR 465–690 class after a long power-off) boots with a wrong date; every check that shift lands in the wrong Z-report, and in PL/RO/UA on the wrong fiscal day. ReassignBusinessDay as an audited admin repair is treating a systemic failure as an exception.
Fix: business day is leased from the coordinator, exactly like check numbers — the coordinator holds the venue's authoritative day-open event. Extend r04's existing "a device N versions behind may take orders but may not tender" rule to cover clock skew: if |device_wall − coordinator_hlc_phys| > 5 min, the device may open checks and take orders but may not close or tender. ~0.3 eng-months. [estimate]
#C6. "Time-varying price lists are selected on-device by now() in the location timezone" (r05 §3.9)
Same root cause, worse consequence. Because r04 snapshots unit_price_minor onto the line and never recomputes, a wrong-clock device prices happy hour incorrectly and that wrong price is then immutable and fiscalized. Fix: coordinator broadcasts an authoritative price-list epoch with the 1 s heartbeat; the device's own clock is only trusted for the sub-minute case. Folds into C5's 0.3.
#C7. "Two devices driving the same card terminal — annoying, self-limiting" (r04 §2.6)
Not self-limiting. A semi-integrated terminal that receives a second collectPaymentMethod while the first is presented can cancel the first after the card was tapped and before your app receives a result. Outcome: an authorisation exists at the PSP with no local payment record — money taken, check shows unpaid. This is the worst state the system can reach and it is a table stakes bug, not an edge case.
Terminal ownership must be a lease with the same fencing epoch as print dispatch, and a nightly reconciliation job must pull PSP transactions by idempotency key and surface orphans. r04 costs this as "+1.0 if you also elect for payment-terminal ownership arbitration". It is not optional. Move the +1.0 into the base.
#C8. "The fold_version only advances when all devices have upgraded" (r04 §4.6)
Deadlocks. One tablet lost, stolen, or in a drawer pins the venue to the old fold forever, and you ship two folds to production for a year because your feature flag never flips anywhere. Needs an explicit device-eviction action plus a 14-day auto-evict, surfaced in back office ("BAR-02 hasn't synced in 30 days — retire it?"). ~0.3 eng-months.
#C9. "Sentry Team plan ~USD 26/mo for 50k errors" (r08 §4.5)
Wrong tier for the fleet. One bad release across 500 sites × 6 devices produces 50k events in an hour. Self-host GlitchTip from day one, or budget EUR 300–800/mo. [estimate] Minor, but it is the kind of line that makes an S1 model look cheaper than it is.
#C10. Compose Multiplatform status (r08 §2.2) — correct as written. CMP is Stable for Android/iOS/desktop; the Wasm web target is Beta as of 1.9. [verified — blog.jetbrains.com] No change. The iPad seam is a real option, not a hope.
#C11. "One pricing/tax engine, pure TypeScript" (r05 §2.2) vs "Kotlin + Compose, zero bridge" (r08 §2.4)
These are mutually exclusive and neither document acknowledges the other. r05 makes a single shared money engine a non-negotiable ("if you write it twice you will have penny mismatches forever… you will spend the rest of the company's life debugging them") and recommends pure TypeScript because the client is assumed to be RN/Electron/web. r08 independently proves Kotlin is correct at the counter on the SDK-bridging argument (≈5.5 eng-months of glue avoided) — which makes r05's assumption false.
Three resolutions, all costly:
- Run the TS engine on-device in a JS runtime inside the Kotlin app. This is the bridging tax r08 just spent 5.5 eng-months avoiding, plus a second GC and a debugging surface across two runtimes on a 2 GB device. Reject.
- Rust → WASM core (r05's own alternative, priced at +2 eng-months). Honest cost with FFI on both sides, a third language the company maintains, and Wasm-on-Rockchip validation: +3.5–5.0.
- Kotlin
:domaincompiled to JVM for the backend. +0 on the engine, but it invalidates r05's "TypeScript everywhere" verdict, changes the backend hiring profile, and shaves ~10% off r05's AI-assist multiplier.
My call is the third, but the point is that nobody has made this call, and it silently invalidates one of the two stack chapters. [estimate] Resolve in week one.
#C12. "Card payment: depends on terminal" in the N1 degradation matrix (r04 §1.1)
That cell is a shrug where a number belongs, and it is the single largest commercial risk in the offline story. If the PSP will not permit store-and-forward with a floor limit, then in N1 (WAN down) the product is cash-only — which in DE/NL/AT, where card is 40–70% of covers, is a 40%+ revenue stop, not a degradation. r04 lists this correctly as a blocker but its own matrix renders it as ambiguity.
Mark it red and state the consequence in the sales deck honestly: "orders, kitchen, cash and receipts keep working with no internet; card payments depend on your acquirer." Ukraine's blackout-resilience pitch (r01 §4.4) is genuinely strong and does not need a claim you cannot back. Getting written store-and-forward answers from two PSPs is a two-week task that should happen before month 3.
#C13. Manager PIN hashes ship inside the menu/config snapshot (r05 §5.2)
Correct requirement (offline manager override must work when Wi-Fi drops — that is exactly when it is needed), wrong implementation as described. A "hashed PIN + permission bitmask inside the snapshot" means every device holds an offline-crackable hash of a 4-digit secret for every manager in the org. A stolen handheld (r08 §4.5 correctly calls handhelds the loss vector) yields the entire manager PIN set in seconds at 10⁴ candidates.
Fix, ~0.3 eng-months: derive with a slow KDF (Argon2id, tuned to ~250 ms on the slowest certified SKU), salt per (org, employee), and wrap the whole block with the device-bound Keystore key r08 §4.5 already specifies for sync auth — so a cloned snapshot on another device is undecryptable. Additionally: scope the offline PIN block to managers assigned to this location, not the org.
#Cross-document contradictions worth resolving before month 1
| # | r0x says | r0y says | Cost of the gap |
|---|---|---|---|
| 1 | r05 #4: order domain + offline sync + conflict rules + device number blocks = 7–9 | r04 #1+#3+#5: the same work = 10 | 7–10 eng-months counted twice, or a boundary nobody owns |
| 2 | r05 #6: realtime gateway + KDS/print routing = 3–4 | r01 #2+#5 = 5.0; r04 #7 = 3.5 | ~3–5 eng-months of overlap |
| 3 | r05 #10: Z-report / EOD / cash reconciliation = 4.5–7 | r08 #7: POS cash management = 1.0 | Boundary undefined; the fiscal half is the expensive half |
| 4 | r05 §2.2: money engine in TypeScript | r08 §2.4: counter in Kotlin, zero bridge | 3.5–5 eng-months either way (C11) |
| 5 | r08: 42 eng-months already includes 1.15–1.5× AI assist | r05: 47 → 30–36 applies AI at the bottom; r01 applies none; r04 applies 0.7×/1.0× per item | Any roll-up is denominated in mixed currencies |
| 6 | r04: hub is a reliability upgrade, correctness must never depend on it | r04 #6 still budgets 2.5 for a hub SKU + image + OTA in the base T3 scope | 2.5 eng-months plus a hardware line for a component the design says is optional |
Item 1 alone is larger than every cut on my list. Reconciling the four build-effort tables into one work-breakdown is 0.5 eng-months and must happen before anyone writes code.
#Underestimated effort
Every number below is my estimate from having built the thing, stated against the doc's number.
| Item | Doc | Mine | Why |
|---|---|---|---|
| r04 #1 — event/command model, deterministic fold, conflict semantics, invariants | 4.0 | 7.0–9.0 | Four eng-months for the whole aggregate including split/merge/seat splits/course firing/comps/service charge/tips, with P1–P11 holding under every permutation, is a first-draft estimate. The fold is not hard to write; it is hard to keep commutative. Every business rule you add later (a service charge applying after comp but before discount; per-seat tax splitting on a split check) forces re-derivation of the absorbing/commuting properties across the whole table. That iteration is the cost and it lands in months 4–14, not weeks 1–16. |
| r04 #10 — deterministic simulation harness + P1–P11 + partition injection | 3.5 | 5.0–6.0 | Not a standalone item. Making clock, RNG, network, disk and time injectable is a refactor tax of ~15% on items 1–9, and the doc costs zero of it. A FoundationDB/TigerBeetle-class simulator is not a 3.5-month artifact for a team that has not built one. See "what I'd do instead" for a re-scope that keeps the total and fixes the sequencing. |
| r04 #5 — coordinator, election, epochs, number leasing | 2.5 | 3.5 | +0.4 for the boot-order epoch hazard (see missing failure modes #1), +0.6 for terminal-ownership arbitration moved into the base per C7. |
| r04 total (T3) | 34.0 | 48–58 | Plus the doc's own +7–10 post-first-20-venues hardening, which I agree with and which is not optional. |
| r04 T0 (single-station) | 13.0 | 17–20 | Closer to right than T3, but items 1 and 10 are in T0 too. |
| r08 #13 — back-office web console (10.0, of which menu mgmt 3.0) | 10.0 | 16–20 (menu mgmt 6–8) | The single most underestimated line in the four documents. r05 costs the menu/catalog backend at 9–16 eng-months; the UI for variant matrices, nested modifier groups with free-count policies, half-and-half portion masks, per-channel price lists with recurrence rules, 86 overrides, per-station routing, per-station language, image pipeline, and publish/diff/rollback is not 30% of that. Incumbents run 6–12 engineers on menu management alone. |
| r01 #5 — printer health, failover, dedupe, telemetry | 2.0 | 3.5–4.5 | The doc itself says the naive version ships in 0.7 and generates angry tickets forever — then prices the correct version at 2.0. Exactly-once across two push protocols, one pull protocol, coordinator failover with epochs, and a verify queue is not 1.3 eng-months of delta. Partially double-counted with r04 #7 (3.5); net new here is ~2.0. |
| r01 #10 + r08 #18 — device management / fleet agent | 2.0 (or 0.8) + 3.0 | 4.5–5.5 net | Writing the agent is the cheap part. The cost is the provisioning failure taxonomy: device already set up (Device Owner permanently unavailable), captive-portal Wi-Fi, WPA2-Enterprise, DPC download blocked by venue firewall, a Sunmi ROM whose setup wizard doesn't expose the 6-tap QR path, zero-touch not enabled by the reseller. Each is a support-blocking dead end. The 25–35 min happy path is real; the 20% of installs that miss it are the budget. |
| r05 #2 — menu/catalog engine (S1-cut) | 9.0 | 11–13 | Half-and-half portion_mask, free-count-with-policy, nested groups with cascade-remove, and ref_variant_id inheritance interact combinatorially. The golden-snapshot corpus that makes this safe is itself ~1.5. |
| r05 #17 — platform: CI/CD, IaC, observability, on-call, DR, load test | 4.0 | 5.5 | Doesn't include the CI-on-real-hardware rig (r04 #11, 2.0) which needs someone to keep it green — budget 0.15/month forever. |
Two structural under-counts nobody itemised:
- The overlap reconciliation itself. Whoever owns the plan has to define the boundary between r04's sync core, r05's order domain, r01's print pipeline and r08's POS UI. That is 0.5 eng-months of architecture work that must happen before anyone writes code, and it will move 12–18 eng-months from one column to another.
- Fixing the Kotlin/TypeScript shared-engine contradiction. 3–5 eng-months whichever way it resolves (see below).
Honest S1 technical scope, POS + back office + KDS + LAN-only raster printing + fleet agent + one fiscal regime, T0→T2, Android-only, one country, no CRM: 62–74 eng-months. Against 3.5 effective engineers × 24 months = 84, that leaves 10–22 for CRM, integrations, and everything unplanned. r06 wants 55 for the CRM. The arithmetic says: either the CRM is a thin loyalty ledger (8–12 eng-months) in v1, or the POS is not full-service. Pick one before writing code.
#Over-engineering to cut
Named cuts, with the trigger that reinstates each.
| Cut | Saves | Reinstate when |
|---|---|---|
| T3 dedicated hub (r04 #6) | −2.5 eng-months + a hardware SKU, an RMA path, a second OS to patch, an A/B OTA pipeline, and "which Pi image is this venue on" as a support question | Never for S1. A Pi 5 with NVMe-on-PoE-HAT in a 45 °C cabinet is a truck roll waiting to happen, and r04 already proves correctness must not depend on it — so it buys only coordinator reliability, which T2 election delivers in <8 s. The seam is the coordinator interface you build anyway. |
| Automerge for order notes + floor-plan editing (r04 §5.1) | −0.5 and one WASM dependency in the counter app | Two people editing the same free-text note within 10 s is an acceptable LWW loss; the floor-plan editor is single-user back office. Reinstate if you ever ship collaborative floor-plan editing, which you won't. |
| Text fast-path printing; keep raster only (r01 #1) | −1.0, and it deletes the entire code-page matrix (CP852/1250/866/1251) which is where CEE bleeds | Cost is ~2–4× slower per ticket — a 40-line kitchen ticket on a TM-U220II goes from ~0.8 s to ~2.5 s [estimate]. Acceptable everywhere except a 500-cover QSR bar. Reinstate on the first customer who measures it. |
| Scales (r01 #9) | −1.0 and the WELMEC 7.2 notified-body tail risk entirely | First deli/bakery customer who will pay for it, and only price-computing legal-for-trade scales. |
| Label printing → ZPL only (r01 #8) | −0.6 | First customer who needs the Brother DK small-label form factor. |
| Owner mobile app → PWA + web push (r08 #14) | −1.5 and two app-store release trains | Web push works on iOS 16.4+ for home-screen PWAs and everywhere on Android. You lose reliable iOS background push, which for "today's sales" is fine. Reinstate at ~200 sites if engagement data says the app matters. |
| NATS JetStream (r05 §2.4) | −0.5 and a 3-node cluster on the on-call rota | The doc says Postgres LISTEN/NOTIFY is genuinely fine to ~200 locations. r09's own S1 model reaches breakeven at ~830 sites in year 4. Do not operate a message bus for a workload you will not have inside the runway. Reinstate at 200 locations. |
| ClickHouse (r05 #11) | −1.5 and a second database to operate | Same trigger: read replica + materialised views to 200 locations. |
| Second cell / data residency (r05 #18) | −2.0 | The doc says "do it before the first US customer, not after" — for S1 that's a 2.0 eng-month bet on a customer you don't have. Do it when a signed DE/US enterprise contract requires it, and price the delay into that deal. |
| Inventory (r05 #8) | −7.0 | Already cut in the S1 scope; agreeing loudly. Ship theoretical COGS. |
| iPad target (r08) | −6 to −10 | Beachhead is CEE; Android AIOs at EUR 465–690 dominate. Keep the CMP module seam (free), activate never in v1. |
Total identified cuts: ~24 eng-months, of which ~13 come out of the S1 critical path. Every one has a named reinstatement trigger, which is the difference between scoping and hoping.
Do NOT cut, despite temptation:
- The property suite (subset of r04 #10). Cutting it is the worst decision available, as the doc says.
- The production divergence detector (r04 §6.7, 0.5). Cheapest insurance in the four documents. It is how you learn about a fold bug from telemetry instead of from a restaurateur who has already fired a server.
- The physical device lab (EUR 6–10k, r01; EUR 5,900/rig, r04). Without it every device estimate in these documents is fiction, and both documents say so.
- The fractional product designer (r08, EUR 2.5–4k/mo). A POS without one does not sell.
#Missing failure modes
Twelve things that will happen and that nobody wrote down.
1. Epoch reuse after a whole-venue power cut → double-fire at scale.
r04 stores the coordinator epoch "durably on each participating device". After a full venue power loss, devices boot in arbitrary order and the first two to see each other elect at max(persisted_epoch). If a device's epoch fsync did not land before power loss — and on cheap eMMC it frequently does not (see #2) — two devices can legitimately believe they hold epoch N, and the fencing token stops fencing. This is the mechanism behind r04 §7's "Month 10 — the double-fire Friday", and the design as written does not prevent it. Fix: derive the coordinator identity from (persisted_epoch, boot_id, monotonic_start); a coordinator claim requires ack from a strict majority of reachable peers, or cloud confirmation when WAN is up; in a 2-device venue with no WAN, tiebreak by lowest UUID and the loser refuses to dispatch prints for 60 s. ~0.4 eng-months.
2. fsync is a lie on the hardware you are reselling.
The entire intent-log design in r04 §4.4 assumes fsync means durable. On cheap Android AIO eMMC and on Raspberry Pi SD cards it often does not. r04 already specifies a smart PDU in the lab — the test is: write intent, cut power at 1,000 random offsets, count survivors. If fsync lies on a certified SKU, the mitigation is the monotonic per-printer ticket number printed on the paper (r01 already has it) plus never trusting the intent log alone. Run this test in week 3, not month 14.
3. Clock skew corrupts fiscal signatures, not just ordering.
r03/r04 solve ordering with HLC and stop. Both DE TSE and UA PRRO embed a timestamp in the signed payload. A device three hours off signs receipts the tax authority will reject or flag. The fiscal adapter must refuse to sign when |device_wall − trusted_time| > threshold, where trusted time comes from the TSE's own clock (Swissbit has one) or last-cloud-sync + monotonic. Compounds C5.
4. Nightly router reboot = nightly failover storm, inside the OS update window.
Many consumer ISP CPE reboot nightly at 03:00–04:00 by default. That is inside a late bar's business day and inside r08's recommended setSystemUpdatePolicy(TYPE_INSTALL_WINDOWED, 03:00–05:00). So on those sites, every night: link drops, all tablets lose the coordinator, an election fires, and Android may simultaneously be applying an OS update. Three fixes, all cheap: (a) the OS update window is venue-configured relative to business-day close, never hard-coded; (b) suppress elections for 120 s after a link-down that affects all peers simultaneously — if you lost everyone, you didn't lose the coordinator, you lost the network; (c) never schedule automatic EOD close inside that window.
5. The KDS that silently stops receiving.
r05 §2.4's per-location monotonic seq with gap replay is correct, but there is no liveness requirement anywhere in the four documents. A KDS whose socket is in CLOSE_WAIT with no OS signal looks alive to the app and dead to the kitchen. Requirement: the KDS renders last-heartbeat age in a corner, changes the entire screen background after 45 s without a server heartbeat, and the coordinator alarms when a station holding open tickets stops acking. ~0.3 eng-months, and it is the difference between a chef trusting the screen and going back to paper permanently.
6. Reprint storm after a station failover. r01's fallback chain (primary → secondary → expo → KDS → manager push) has no rate limit and no ordering guarantee. When a kitchen switch dies, 40 open tickets fail over to the expo printer at once and print in queue-drain order rather than fire order. Need per-station ordering by fire time and a cap: "40 backlogged tickets — print all / print summary?".
7. Device event-log growth makes month 9 slow at your best customer.
r08 budgets 250 MB PSS and ≤1.5 s cold start. Nobody costs the log itself: at 40–120 events/check and ~1,400 checks/day, a busy venue produces 100–170k events/day. r04 says keep 90 days on device — that is 9–15M events under SQLCipher on a 2 GB RK3568, and P10 (replay from empty) takes minutes. Requirement nobody stated: on-device retention is 7 days of closed checks + all open checks, cloud is the archive, hard alarm at 500 MB DB size. Without it, "it's slow on Fridays" arrives in month 9 and is unfalsifiable.
8. Config rollback via a stale CDN edge undoes your kill switch. r08's Friday mitigation depends on a remotely-flippable flag evaluated from a locally-cached config. Missing: the config bundle must be signed and carry a monotonically increasing version, and the device must refuse to apply a lower version. Otherwise a device that reconnects to a stale edge silently rolls the kill switch back mid-service — and you will spend two hours believing your mitigation didn't work.
9. GDPR erasure vs. the rendered ticket raster. r04 and r06 both handle PII-in-the-log via a crypto-shreddable side store. Neither notices that r01's recommended raster-first print path renders the guest name into a bitmap that then sits in the print outbox and the reprint buffer. Rule: tickets render at print time from IDs; rendered bitmaps are never persisted beyond the retry window (cap 24 h).
10. The divergence detector fires and you cannot reproduce it. r04's simulator prints a seed on failure — excellent. Production divergence has no seed. Pair the detector with automatic event-log bundle upload on divergence (pseudonymised, per §6.6) or you have built an alarm with no forensics.
11. AI menu-ingestion fan-out as a cost incident. r05's per-tenant monthly spend cap protects against a runaway agent loop on one tenant. The realistic incident is a support engineer re-running menu ingestion for 300 tenants after a prompt change — every call is legitimate, every cap passes, and the bill is 300 × $0.53 plus retries. Need a per-feature global spend cap requiring a human to raise it.
12. SET LOCAL app.org_id outside a transaction (r05 §1.2).
SET LOCAL is transaction-scoped, so it works under PgBouncer transaction pooling — but any query issued outside an explicit transaction gets whatever the pooled connection last had, or nothing. RLS then gives false confidence. Enforce "no query outside a transaction" in the repository layer with the same lint rule that enforces TenantContext, or don't claim RLS as a backstop.
13. The ISP swaps the router and re-IPs the whole venue — including the coordinator.
r01 §4.5 ranks "DHCP lease changed / router replaced by ISP" as ~20% of printer incidents and solves it for printers with static IPs outside the pool + MAC-keyed rediscovery. Nobody applies the same reasoning to peers. After a router swap the new DHCP scope may be a different /24 entirely (192.168.0.0/24 → 192.168.1.0/24), which invalidates every persisted last-known-IP, breaks the /24 sweep fallback (wrong subnet), and — if the venue is on the T3 hub — strands a headless device with no screen to tell anyone. Peer records must key on device_id + cert fingerprint, re-resolve via cloud rendezvous on subnet change, and the app must detect "my subnet changed" as a first-class event that triggers full rediscovery and a manager notification.
14. Split-payment concurrency at the end of service.
r04's RecordPayment rule is right (additive, dedupe only by payment_id, excess → OVER_TENDERED), but the real-world sequence is: guest A taps card on terminal 1 for "their half" computed at T; guest B simultaneously tenders cash at the till for "the rest" computed at the same T. Both are valid, both commit, and the check is now over-tendered by the tip on A's card. OVER_TENDERED with "REFUND DUE €12.40" is the correct state but a terrible experience at 22:30 with two guests standing there. Needs a soft advisory lock at tender time (r04's own §3.4 "advisory badge" concept, applied to the payment screen specifically) plus a bounded "amount remaining" that is leased, not computed. ~0.4 eng-months, and it is the most common real split-check failure I have seen.
15. Non-GMS push transport dies to Android background restrictions.
r01 §1.3 costs "your own MQTT/WebSocket foreground service" at 1.0 eng-month for non-GMS devices. What that number omits is that OEM battery managers and successive Android background-execution tightenings kill long-lived foreground services on exactly the cheap AOSP ROMs you are reselling — the well-known "the tablet stopped getting online orders overnight" failure. Mitigations that must be in the 1.0 (they are not): a WorkManager watchdog that re-arms the service, an AlarmManager exact-alarm heartbeat as a second belt, an on-screen "last contact" indicator, and a server-side alert when a device with an active shift goes quiet for >5 min. Realistically 1.8–2.2, and it is the strongest argument for paying the EUR 30–260 GMS/EDLA delta r01 already recommends.
16. Nobody can answer "what did this venue actually do last Friday?" for the first 20 customers. r04 §6.6 (production replay) is listed as a support superpower and costed inside item 13 (1.5, support/ops tooling). In practice the replay pipeline is the last thing built and the first thing needed, because customers #1–20 are exactly the cohort that finds the fold bugs. Pull the event-log export + replay-into-simulator path forward to month 5 (before pilot venues), not month 14. It is ~0.6 eng-months of the 1.5 and it converts every early support call from a two-day archaeology exercise into a 20-minute investigation — which is also how you keep those first 20 accounts.
#What AI-assisted development can and cannot compress
r09's blended "~30%, 55 → 38 eng-months" is roughly right in aggregate and badly wrong in distribution. It will make you believe the offline core is 30% cheaper. It is 10% cheaper. Split by work item:
| Speedup | Work |
|---|---|
| 1.6–2.2× | ESC/POS command emission and the ticket layout DSL; ZPL; ePOS-Print XML serialisation; CBOR envelopes; SQLite schema + forward-only migrations; the outbox/backoff machinery; REST/OpenAPI CRUD and generated clients; accounting/payroll export adapters given one worked example each (DATEV, Fakturownia, QBO); back-office React CRUD; i18n plumbing; property-test harness boilerplate. And the highest-value item in the whole plan: the AI menu-ingestion pipeline itself. |
| 1.2–1.4× | The fold's business rules once the conflict table is written by a human; the price/tax engine's rule evaluation given a golden corpus; KDS UI; the print routing engine; the metric layer. |
| ~1.0× (no help) | Deciding the conflict semantics table in the first place. Every device-specific quirk — the Sunmi AIDL service unbinding after 4 h idle, an Elo panel reporting the wrong Configuration.orientation on a landscape-locked screen, a TM-U220II needing ESC @ after a cutter jam. Exactly-once printing under real power cuts. Election/fencing correctness. Apple / Managed Google Play / PAXSTORE / acquirer approval queues. On-site install failures. Anything requiring a physical device on a desk. |
| Negative | Distributed-systems code. AI-written folds look right and pass the tests you asked for. The failure I have watched: an agent produced a fold satisfying P1–P3 and quietly violating P4 (permutation invariance) because it iterated a hash map. Hard rule: the property suite is written by a human before the fold exists, and the fold is only ever accepted by the property suite, never by review. |
Honest per-document multipliers: r01 ≈ 1.25×, r04 ≈ 1.10×, r05 ≈ 1.50×, r08 ≈ 1.35×. Apply those before adding anything up — and note that r08's 42 already has 1.35× baked in while r05's 47 does not.
#What I would do instead
Five decisions, week one, before the first Gradle file.
- Native Android only at the counter. No browser, no WebView, no iPad in v1. This makes r01 §2.5's entire browser chapter moot, deletes C1's LNA problem from the critical path, and removes the frozen-WebView risk on non-GMS SKUs. Buy GMS/EDLA SKUs regardless, per r01 §1.3.
- The shared pricing/tax/fold engine is Kotlin, compiled to JVM for the backend. r05's "TypeScript everywhere" loses. Consequences to accept honestly: the backend is JVM (hiring in Kyiv/Warsaw is fine), the AI-assist multiplier on backend CRUD drops ~10%, and the back office is React talking to a generated OpenAPI client rather than sharing a
@rb/domainpackage. Cost of the alternative — writing the engine twice — is the thing r05 itself says will "cost you the rest of the company's life". Do not leave this unresolved past week one; it is a 3–5 eng-month decision either way. - T0 (single station) ships first. T2 (elected leader, no hub SKU) second. T3 never. r04's own market question ("what fraction of the beachhead runs 2+ stations?") is answerable with 30 discovery calls in two weeks; make that call before month 3, not month 12.
- KDS-first, printer-second. Sell the KDS at cost if you have to. A KDS is a stateful networked peer, so delivery is idempotent and confirmable; a push printer has an irreducible ambiguity window. Selling a EUR 250–450 panel instead of a EUR 280 printer removes the hardest correctness problem in the product. This is the single best engineering-driven commercial decision available.
- Ship the router and the AP as a hard requirement, not a recommendation. r01 recommends it; r04 shows that AP/client isolation makes peer sync impossible (not merely undiscoverable — a cloud-published LAN IP is useless when L2 is blocked). Either the bundle includes a RUT241 + a validated AP and you refuse to install without them, or you owe 1.5–2.0 eng-months for a cloud-relayed peer path that nobody costed. Take the router.
Sequencing, S1, 12 months to first paying venues.
| Months | Work | Why here |
|---|---|---|
| 0–2 | Fold + hand-written property suite (P1, P3, P4, P7 only) + AI menu ingestion | The property suite must exist before the fold, per the AI section. Menu ingestion is simultaneously the demo, the onboarding-cost killer (r05 §7.1: 40 h → 3–4 h), and the cheapest thing to build. |
| 2–5 | POS shell, order/ticket UI, LAN-only raster printing, cash management, X/Z | The narrowest thing that can take money. |
| 5–7 | Back-office menu management + one fiscal regime (CZ or UA) + fleet agent | Menu management is the buying evaluation. One regime, chosen for zero certification gate. |
| 7–9 | Five pilot venues with an engineer physically present for every service for the first two weeks | Zero eng-months of "development" and ~1.5 of calendar, and it is the highest-ROI activity in the entire plan. Every non-obvious requirement in r08 §5 came from a shift, not a spec. |
| 9–12 | T2 LAN peer + the injected-fault simulator, scoped by what actually broke in months 7–9 | Build the simulator against observed failures, not imagined ones. |
Re-scope the simulator rather than cutting it. Do not attempt a FoundationDB-class simulator in month 2. Month 2 gets the pure fold plus P4 (permutation invariance), P1, P3 and P7 as plain property tests over the fold — 0.8 eng-months, 60% of the value. The injected clock/network/disk simulator lands months 9–12, informed by five venues of real failures, at 4–5 eng-months. Same total, far better sequencing, and you stop the money bugs before your first customer instead of after.
Two non-negotiables: the physical device lab (EUR 6–10k) and the property suite before the fold. Everything else on my cut list is negotiable.
One hire before a fourth engineer: a field/install person, by venue #10. r08 says this and is right; it is the item founders skip and then spend two engineers' time on install-day phone calls.
The number that decides the company. Honest S1 technical scope — POS + back office + KDS + printing + fleet + one fiscal regime, Android-only, T0→T2, one country, no CRM — is 62–74 eng-months against 84 available. That is not a plan with slack; it is a plan with 10–22 eng-months for CRM, integrations, hardening, and everything that goes wrong. r06 wants 55 for the CRM alone and r07 wants 28.5 for integrations. The arithmetic does not close, and no amount of AI assistance closes it, because the parts AI helps least with are exactly the parts on the critical path.
So: the CRM in v1 is a signed-voucher loyalty ledger and a phone-number capture at the till (8–12 eng-months), or the POS is counter-service only. Choosing neither is how the other POS I worked on died — not from a bad architecture, but from a good architecture that ran out of months.