Appearance
26.0.1 — Scope
Status: 🟡 scoped, re-based for the consumer delivery · Target: 2026-09-10 · Scope freeze: 2026-09-07 · Type: train_biweekly
RE-BASED 2026-09-04 BY OWNER DECISION — READ THIS BEFORE THE SECTIONS BELOW
The owner's instruction was "rebase 26.0.1 completely for Consumer side delivery and release". 26.0.1 keeps its version number because nothing has ever shipped through this system — the production state page says so, and it names 26.0.1 as the release that will be recorded there first. Opening a 26.0.2 would have created two release records for one production deploy, which is the duplicate-source-of-truth class this repo has already paid for once (QRS-249).
Gates G0-G5 are all reset to pending and approvals is empty. G0 had passed for a different release; G4 binds an approval to a commit SHA and a manifest hash, so inheriting a pass for work this record no longer describes would defeat the mechanism rather than save a step.
The change manifest was NOT emptied, and that is deliberate. Its 121 records declare work that is in the repo and therefore deploys with this release — see The carried baseline below. Deleting them to make the record read as purely consumer would UNDECLARE real production changes, which is what the sixth rule forbids and what deploy-prod.yml refuses to apply.
The previous scope is preserved verbatim from Objectives onward as the superseded record.
What this release delivers
The plan of record is consumer/end-to-end-plan.md, approved 2026-09-04: §1 every design artboard with a disposition, §3 features F1-F18, §4 tables/RPCs/EFs by status, §10 the architect review R1-R38, §11 phases P0-P9, §12 the decisions adopted.
- Ship the CONSUMER product: a person signs in by WhatsApp OTP, claims their permanent qrsetu.com address, builds a Marriage Biodata, publishes it, releases it to named people, and a family reads it in the app or in a browser — every step against a real backend, no stubs in the path.
- Ship the surfaces that make that reachable rather than merely present: the consumer tab bar and centre button (there is none today), the round-40 identity home, Scan and verify, the eight Account views, notifications, the two personal QR makers, and the participant half of Meetings.
- Ship the marketplace OFF, and off means ABSENT — no "coming soon" tile ever stands in for a section, so this release needs no marketplace backend at all.
- Carry the merchant backend built ahead of this release to production for the first time. It is not this release's deliverable, but it is in the repo and it deploys with it, so every one of its change records stays declared. See 00-scope.md, "The carried baseline".
- Be the first release to reach production through the governed path, and prove the path by using it.
Scope items
Every row is an existing tracker id; the release gate verifies that on every run — and found a real defect doing so on 2026-09-04, since it had been matching exactly three digits and could not see a four-digit id at all (QRS-1041).
| Id | What this release delivers for it |
|---|---|
| QRS-1017 | Screen ledger re-transcribed from design round 40 so the printed count is true |
| QRS-1024 | Five unregistered consumer artboards written back to the design registry |
| QRS-981 | Android upload keystore — the release build is debug-signed today |
| QRS-1013 | Two Meta UTILITY templates submitted (biodata subject notice, meeting update) |
| QRS-917 | primary_context reaches the server at sign-up, so a consumer is not provisioned as a merchant |
| QRS-936 | auth.users.phone carries its E.164 + prefix |
| QRS-998 | The account is created on OTP VERIFY, not on send |
| QRS-909 | delete_account becomes a soft delete with a documented, tested cascade |
| QRS-1016 | manage-media gains an owner scope — a consumer cannot upload anything today |
| QRS-1019 | BOTH media constraints widened together (scope XOR and purpose-matches-scope) |
| QRS-1018 | feature_grants gains its exactly-one-scope CHECK, via the architecture-change protocol |
| QRS-1020 | Reserved route segments single-sourced and bound to the SQL seed by a test |
| QRS-921 | Rate limiting for anonymous reads and access requests |
| QRS-1033 | Biodata backend: schema, manage-biodata, biodata-read |
| QRS-1027 | Consumer chrome: tab bar, centre button, More sheet, header |
| QRS-1006 | ConsumerHome rebuilt on the round-40 identity-hub stack |
| QRS-990 | The consumer-home parity contract closed out against round 40 |
| QRS-1028 | My QR setu, the identity home |
| QRS-1029 | Marriage biodata: the editor |
| QRS-1030 | Marriage biodata: people and access |
| QRS-1031 | Marriage biodata: the in-app reader |
| QRS-1032 | Marriage biodata: the public page and the /<slug> greeting (apps/web) |
| QRS-1034 | The BrandQr primitive, sharing and deep links |
| QRS-1035 | Scan and verify, on the existing scanner shell |
| QRS-1008 | The dead /consumer/scan link resolved by the screen that belongs there |
| QRS-1036 | Consumer account: the eight views |
| QRS-1037 | Consumer notifications: new kinds and per-account read state |
| QRS-1011 | Notification read state moves from the device to the account |
| QRS-1038 | Personal QR makers: Wi-Fi and Link |
| QRS-1039 | Meetings, the participant half |
| QRS-1015 | manage-chat — the chat write path that no Edge Function provides today |
| QRS-992 | The identity QR encodes the build environment origin instead of a hardcoded one |
| QRS-924 | Marathi reviewed by a human across every consumer namespace |
| QRS-1021 | Decision: "message the family" routes to WhatsApp at launch (D-v) |
| QRS-1022 | Decision: the Link maker ships static, with a design copy correction (D-s) |
| QRS-1023 | Decision: only In app and WhatsApp are shown as channels (D-p) |
The carried baseline
The merchant backend was built before this re-base and ships in the same deploy. It is no longer this release's deliverable, so it has left scope; it has NOT left the change manifest, because the code is in the repo and will be applied to production. Both statements have to be true at once, and keeping them in two different places is what lets them be.
QRS-304 · QRS-305 · QRS-306 · QRS-307 · QRS-308 · QRS-309 · QRS-310 · QRS-311 · QRS-312 · QRS-313 · QRS-314 · QRS-315 · QRS-316 · QRS-317 · QRS-318 · QRS-319 · QRS-320 · QRS-321 · QRS-322 · QRS-249 · QRS-301 · QRS-303 · QRS-299 · QRS-537 · QRS-538 · QRS-539 · QRS-542 · QRS-543 · QRS-551 · QRS-555 · QRS-554 · QRS-559 · QRS-560 · QRS-561 · QRS-566
⚠ Do not read this list as "already in production". Nothing is. It is work that is finished on Dev, declared in 01-change-log.md, and reaching production for the first time with the consumer launch. G3 owes it the same evidence as anything else in the manifest.
What is deliberately NOT in this release
- The marketplace, and off means ABSENT. The design's own default drops every
market: truesection, so no "coming soon" tile ever stands in for one and the release needs no marketplace backend at all. Item feed, vendor feed, item view, vendor view and the order code stay in the tree reachable from a scanned code, and nothing in the approved navigation links them. - Meetings HOSTING. The participant half ships; minting a room needs a Zoom account model and a Marketplace review with no SLA (QRS-1014). The connect gate renders as not connected rather than as a dead control.
- The intro card, My progress, the UPI and Photos makers, chat media and voice. Each is designed and each is deferred for a stated reason in the plan's §1 and §12, not for want of a design.
- Push notifications, SMS and email. There is no token table, no sender and no channel. They are rendered ABSENT on the availability axis rather than as toggles that persist a preference nothing reads.
Superseded — the original vendor-money-loop scope, kept verbatim
THIS SECTION IS THE PREVIOUS RECORD, NOT THE CURRENT SCOPE
Everything below described 26.0.1 before the 2026-09-04 re-base. It is kept because a release scope is a log: superseding it with a banner preserves why the earlier decisions were taken, while editing it away would destroy the only record of them. Read What this release delivers above for what is actually in scope.
Objectives
- Ship the complete vendor money loop. A merchant signs up, builds a Setu Card, lists what they sell, publishes, shares it — and a customer opens the card and pays. Every step against a real backend; no stubs in the path.
- Deploy 15 Aug with payments dark; activate by 20 Aug via the kill switch.
- Establish a governed, auditable path to production and prove it by using it.
- Reconcile the branch model so
deploy-prodcan run at all.
Scope
23 items. Every one references an existing QRS-### — releases never mint their own ids, and check:release verifies each exists in the tracker.
| QRS | Item | Owner | Acceptance criteria | Depends on |
|---|---|---|---|---|
| QRS-299 | Manifest-hash scope fix | maintainer | Updating targets[] mid-deploy leaves the approval intact; editing scope[] voids it. Both directions tested. Done 2026-08-03, 15 tests. | — |
| QRS-301 | reserved_slugs Prod↔Dev reconciliation | maintainer | is_slug_reserved blocks privacy/mumbai/zomato and allows a real merchant slug, on both projects. Dev done 2026-08-03. | — |
| QRS-249 | Prod taxonomy promotion | maintainer | get_business_domains() returns the full set against Prod, and onboarding's industry step renders on a device pointed at Prod | CR-01 |
| QRS-303 | verify_jwt corrected on Type A EFs | maintainer | A request with no Authorization header is rejected at the gateway, proven by a live probe | — |
| QRS-304 | apps/web workspace + PRODUCTION_PATHS | maintainer | apps/web builds and deploys; a change under apps/web/** fails check:release without a Change Record | — |
| QRS-305 | Request-scoped Supabase client | maintainer | Two concurrent SSR requests cannot observe each other's session. Three-surface device pass on apps/mobile, which shares the seam | — |
| QRS-306 | Cloudflare Pages + DNS + SSL + deploy workflow | maintainer | qrsetu.com serves the landing page over HTTPS; the deploy workflow has a public_web component | QRS-304 |
| QRS-307 | Public Setu Card SSR + OG + landing + order list | maintainer | Pasting a real card URL into WhatsApp renders title, description and image. Not a unit test | QRS-304, QRS-305, QRS-308 |
| QRS-308 | is_published + publish toggle + profileService real | maintainer | is_published=false ⇒ 404; toggle on ⇒ 200. pgTAP updated. No backfill | — |
| QRS-309 | Catalog — extend profile_items + item_media + manage-item | maintainer | A merchant's items render publicly on the card; availability_status gates purchasability | — |
| QRS-310 | Catalog editor, merchant-side | maintainer | Add/edit/delete an item with a photo, verified on all three surfaces | QRS-309 |
| QRS-311 | _shared/webhook.ts HMAC kit | maintainer | Signature verification proven against a real Razorpay payload; a tampered body is rejected. ADR-0002's false claim corrected | — |
| QRS-312 | Money data model | maintainer | CHECK (gross = vendor + commission) holds; pgTAP proves anon cannot read order or payout tables | — |
| QRS-313 | create-payment-link EF | maintainer | A tampered amount is rejected; the order row exists before the customer pays; reference_id = order UUID | QRS-309, QRS-312 |
| QRS-314 | razorpay-webhook EF | maintainer | Killing the webhook mid-flow still leaves a complete order row; duplicate events are idempotent; a commission mismatch raises a P1 | QRS-311, QRS-312 |
| QRS-315 | Payments kill switch | maintainer | Demonstrated flipping on and off without a deploy. Ships before the first rupee | — |
| QRS-316 | Anonymous order write via the Worker | maintainer | The order EF rejects a request without the Worker's shared secret; Turnstile blocks a scripted POST | QRS-306, QRS-313 |
| QRS-317 | Reconciler EF + cron | maintainer | Proven to find a deliberately dropped webhook. Cron works on Dev, i.e. the project-URL hardcoding is fixed | QRS-312 |
| QRS-318 | money_health + non-email alerting | maintainer | A zero-webhook hour raises an alert on a channel that is not email | QRS-312 |
| QRS-319 | Banking/Payouts module + API onboarding | maintainer | A vendor self-onboards a payout account end to end; the full account number is never persisted; only activation_status='activated' enables the pay button, checked against the API | QRS-311 |
| QRS-320 | Open-self-serve obligations | owner | Privacy, terms and a named grievance officer are published and reachable before the first public signup; report-abuse works; the takedown runbook is written | — |
| QRS-321 | Card analytics | maintainer | card_viewed is recorded for a real card view, counted in the SSR loader — no public write endpoint | QRS-307 |
| QRS-322 | Festival Vendor discovery + G-D gate | maintainer | Gate proven negatively: a domain_scoped item with no doc ⇒ exit 1 naming the vertical; add the doc ⇒ exit 0 | — |
Divergence-seam check (QRS-297) — asked at G0, not at G3
The platform-exception path is retired, so an item that turns out to be unbuildable on one surface blocks the whole release. Every seam this release touches, with its per-surface answer:
| Seam | Android | iOS | Web PWA |
|---|---|---|---|
| Image upload (QRS-310) | native picker | native picker | <input type="file"> |
| Share card (QRS-307) | RN Share | RN Share | navigator.share → clipboard fallback |
| Copy link (QRS-307) | ⚠ expo-clipboard is not a dependency — surface its weight before adding | same | navigator.clipboard |
| Open card / deep link (QRS-307) | expo-linking | expo-linking | window.open |
| QR display | pure-JS encoder through the already-bundled react-native-svg — not a seam | same | same |
| Camera / QR scan | not in scope this release | — | — |
Notification permission and geolocation are deliberately NOT in this release — they belong to onboarding completion (QRS-326), which is 26.0.2 scope. Web is an honest no-op for notifications (createPort.ts) and there is no expo-location dependency, so both need a recorded per-surface answer before that work starts rather than during it.
Success criteria
Release-level and measurable, checked at G5.
- [ ] A merchant completes signup → card → catalog → publish → share, and a customer opens
qrsetu.com/<slug>and sees it render server-side with a correct WhatsApp preview - [ ] A real payment reaches a real vendor's real bank account, split by Route, and the vendor sees the order in the app
- [ ] Payments activated by 2026-08-20 by flipping the kill switch — no deploy, no gate, no review
- [ ] The reconciler finds a deliberately dropped webhook; a zero-webhook hour alerts off-email
- [ ] A KYC-inactive vendor cannot accept payment, asserted against the API status
- [ ] pgTAP proves
anoncannot read any order or payout table - [ ] Three-surface parity verified per feature as it is built, not batched at G3
- [ ] Privacy, terms and a named grievance officer published before the first public signup
- [ ] Prod and Dev agree on migrations, functions, RLS policies and reference data — re-run the 2026-08-03 drift fingerprint
Milestones
| Date | Milestone | Gate |
|---|---|---|
| 2026-08-03 | Scope agreed — roadmap reconciled into RMS | G0 ✅ |
| 2026-08-12 | Scope freeze — only P0/P1 defects in accepted scope after this | G1 |
| 2026-08-14 | Code complete / RC | G2 |
| 2026-08-15 | UAT + readiness review, three-surface parity evidence assembled | G3 |
| 2026-08-15 | Go/no-go — recorded in a separate session from the request | G4 |
| 2026-08-15 | Deploy. Store loop public, payments DARK | — |
| 2026-08-18 | Live ₹1 settled into a real bank account | — |
| 2026-08-20 | Payments ACTIVATED via kill switch — no deploy | — |
| 2026-09-03 | Closure after the 48h observation window + follow-up release cut | G5 |
Scope-change log
Every request that arrived after G0, accepted or not.
| Date | Request | Triggers fired | Decision | Rationale |
|---|---|---|---|---|
| 2026-08-03 | Banking module as core product surface, API payout onboarding | new migration · new EF contract · per-platform behaviour · store policy | accept | Owner decision. Open self-serve makes manual linked-account creation unworkable — there is no bounded cohort to hand-create for. Cost +0.75d and it retired the manual-onboarding fallback, leaving one slip lever. |
| 2026-08-03 | Fully open self-serve publishing at launch | console-only class · new EF | accept | Owner decision. Cost was ~0.9d rather than the ~3d first estimated, because reserved_slugs, is_slug_reserved, the write-once trigger and 12 pgTAP assertions already existed. The real cost is the owner-track grievance officer. |
Out of scope
Named explicitly, so "we forgot" and "we decided not to" stay distinguishable.
Deferred to 26.0.2 — this is the release's only slip lever
The four stub retirements are the only scope not on the vendor money loop, which is exactly why they are out rather than in. Total 3.5 work-days.
| Item | Destination |
|---|---|
QRS-323 settingsService real | 26.0.2 (~3 Sep) |
QRS-324 accountService real — ⚠ holds Apple 5.1.1(v) account deletion | 26.0.2 |
QRS-325 remindersService real — blocked on QRS-286 | 26.0.2 |
| QRS-326 Onboarding completion | 26.0.2 |
Why they are out from the start rather than cut later
Scope is ~32.25 work-days against 18 calendar days — ~1.79× against a stated pace of 1.5×, a gap of ~5.25 days. release.json has no state for shipped-minus-four-scope-items; partially_deployed covers targets, not scope; and dropping accepted scope after G1 re-hashes the manifest and voids the G4 approval (QRS-299).
So declaring them out now is what makes the slip valve expressible at all. If the pace holds they get pulled forward — which is a normal scope-change-log entry. Adding them back under date pressure after G1 is the thing this structure exists to prevent.
⚠ QRS-324 is not freely deferrable if iOS submits — in-app account deletion is an Apple review requirement. Either it comes back into 26.0.1 or iOS does not submit in this release.
Not in this release at all
| Item | Why |
|---|---|
| Admin panel + RBAC | 26.2.0. ADR-0006 is 0% built and it is the real cost. Until then, takedown is a documented SQL runbook |
| Desktop DOM merchant layout | 26.3.0. R1 merchants are phone-first; building it now buys nothing and costs a fortnight |
| Leads · Chat · Contacts | 26.4.0-26.5.0 |
| Real dashboard | Needs the ADR-0010 read model. Stays on demo data, honestly labelled via the existing isDemoData → DemoChip mechanism |
| Email OTP | Blocked at the SMTP layer (QRS-285). Google sign-in is the only auth path |
| Refunds, disputes, reversals in code | 26.1.0. Deliberately manual in v1 with a written runbook — that is what makes safe payments buildable in this window at all |
Advance-hold (on_hold) | Not available at payment-link creation; settlement-hold is a post-payment PATCH. A stall vendor taking an advance wants the money now |
| Digital Menu, billing | R2 scope / ADR-0002 |
| The 9 orphan Feedback-Forms Edge Functions | Live in Prod with no source in the repo (QRS-302). Recovery is tracked; conformance is not R1 work |
| Feature flags (QRS-296) | 26.2.0. The payments kill switch covers this release's one need |