Skip to content

Consumer release build — reconciliation against the latest design (2026-09-04) ​

⚠ THIS IS A LOG. IT RECORDS WHAT WAS TRUE ON 2026-09-04, AGAINST THE DESIGN PULLED LIVE THAT DAY.

Design state was read from the Claude Design prototype project through DesignSync, never a local cache: SCREENS.md (ConsumerHome at round 40), ConsumerHome.dc.html, MyQRSetu.dc.html, Biodata.dc.html, BiodataView.dc.html, Account.dc.html, Notifications.dc.html, ScanVerify.dc.html, setu-card/BiodataPage.dc.html, consumer-data.js, biodata-core.js, qr-registry.js, qr-verify.js, my-qrsetu/biodata.spec.md and my-qrsetu/Integration.dc.html. Repo state was measured from apps/mobile, packages/*, supabase/migrations and the parity contracts, by reading the files. Do not edit this forward; run a new one.

Owner's brief (2026-09-04): a consumer build that is functionally complete for ConsumerHome · Marriage Biodata · Consumer Profile · Consumer Settings · the core flows that make those run end to end, ready for release, without expanding marketplace functionality. Upcoming planned change: the bottom bar's Orders → Meetings; build no new dependency on Orders.

0 · The finding that reorders the work ​

🔎 THE MARKETPLACE IS OFF BY DESIGN DEFAULT, AND OFF MEANS ABSENT — WHICH REMOVES HALF OF HOME

SCREENS.md (round 40): "The marketplace is conditionally OFF, and off means ABSENT: platform.capability.consumerMarketplace (default false, Mission Control) gates location + search, the action strip, featured, trending, the category mosaic, saved and recent, the order book and shop scans. No 'coming soon' tile ever stands in for them." consumer-data.js confirms it: homeSections(present, { marketplace }) drops every section flagged market: true when it is off.

Two consequences, both in the owner's favour:

  1. The consumer release build needs NO marketplace backend at all. Item feed, vendor feed, saved store, area picker, category taxonomy and the platform capability table — every one of the blocked and gap rows on the current consumer-home.json contract that named them — falls out of scope for this release by the design's own switch. That is exactly the owner's instruction restated as configuration.
  2. The current ConsumerHomeScreen is not "21 of 39 rows" — it is the wrong shape. It renders the round-16 discovery stack (search · actions strip · featured · near you · the QR tools row), and round 40 retired the QR tools row and the action strip "as duplicate navigation". Below the identity card, almost nothing currently on the screen survives into the design being shipped. The rebuild is the identity hub, and it is the first client task.

What round 40 puts on Home, in order (HOME_SECTIONS), with the marketplace off:

SectionReadsBackend it needs
identity (pinned)greeting · address + branded QR + copy · pulse chip (N need you / N live) · Share my card · My QR setu · start block when nothing is publishedget_my_slug ✅ · biodata access rollup ❌
stories — "Your QR experiences"one ring per activated experience, state from personalStories(); dashed Make newbiodata record ❌
focus — "Waiting on you"snap carousel of ownerPrompts(): a request waiting, a release lapsing, a removal being honouredbiodata shares / requests / removal ❌
todaythe next accepted session · daily check-in · unread coach announcementmeetings ❌ · progress ❌ — not this release
quickMake — "Make in one tap"Wi-Fi · UPI · link · photos, from FREQUENT_MAKERSnone (client-only makers)
progressthe profile journey: journey() percentage ring, four milestones, thinnest sectionbiodata record ❌
yourCards — "Your QR setu"the creation cards with access facts (never scan counts)biodata record ❌
createa brand-plate lead card for the first kind, staggered tilesnone
weekRecap—meetings / progress ❌ — not this release

1 · Screen-by-screen reconciliation ​

Verdicts: ✅ built and matches · 🟡 partial · ❌ missing · ⛔ blocked (a dependency that must land first, named).

1.1 The chrome — the tab bar, the centre button, the header ​

ElementDesignImplementationVerdict
Bottom barCONSUMER_TABS: Home · Chats (unread badge) · [centre] · Saved · Orders; Account is a header avatar, not a tab; consumerBarTabs() is the one place the two-and-two split is decidedNo tab bar exists. app/consumer/_layout.tsx is a plain Stack (it was created on 2026-09-03 solely to host the app lock)❌
Centre buttonopens the More sheet: three groups from consumerMoreGroups() — Make something (personal kinds) · QR tools (CONSUMER_QR_MAKERS) · Tools (Scan · My order code · Recently scanned · Share my contact)none❌
Headerwordmark · clock (reminders sheet, count) · bell (notifications, unread count) · avatar (Account)none on Home❌
Orders tabpresent in the design; owner: to become MeetingsOrderCode exists as a screen; no tab— reserve the slot, build nothing new on Orders

This is the single biggest reachability gap. Nine consumer routes exist and nothing navigates to them (QRS-730's shape, still true one layer up). Every screen below is unreachable until this lands, so it is W3, immediately after the schema.

1.2 ConsumerHome ​

Covered in §0. Of the identity card itself: greeting ✅ · address ✅ · branded QR ✅ · Share ✅ · claim sheet ✅ (a stopgap, correctly recorded) · copy control ❌ (expo-clipboard not installed — needs the size callout) · pulse chip ⛔ · My QR setu second action ⛔ · start block ⛔ — the three blocked ones all read the biodata record.

1.3 My QR setu — MyQRSetu.dc.html ​

7 states: Open · In discussion · Concluded · Nothing activated · Address just claimed · Loading · Error. Implementation: none. Structure: identity card (initials, name, address + copy, QR, note, Share my card, gear → Account) · the Intro-card row · Activated (the biodata card: title, address, status chip + lifecycle chip, owner line, three access facts with the line "Access facts, not attention. QR setu does not count views on a person", four actions) · Available (Invitation · Birthday card, locked "On a plan · Managed on qrsetu.com" — which is the fifth rule applied correctly: discoverable, locked, no in-app CTA) · Your identity settings rows → Account views.

Everything except the Activated card and the Intro-card row is buildable with what exists today.

⚠ THE DESIGN HAS REVIVED A RETIRED PRODUCT NAME, AND THIS PAGE HAS TO WRITE AROUND IT

MyQRSetu.dc.html links to a screen the design project calls the Intro card and files under a name containing the word this repo retired on 2026-07-23 (QRS-172) — the check:docs vocabulary gate's term group #1. Its PERSONAL_KINDS id is intro, and its public page is setu-card/BioLinkPage.dc.html. This page refers to the feature only as the Intro card, and the collision is logged as QRS-1007 so the design project can be corrected rather than the repo's gate weakened. It is not in the owner's five priorities and is classed as a later phase below.

1.4 Marriage Biodata — Biodata.dc.html, the editor and the access view ​

One module, two views on ?view=: content (10 states: empty and starting · partly complete · complete · editing a live profile · validation on a required field · saving · saved · loading · could not save · read-only for a co-manager) and people (8 states: nobody yet · a basic link shared and opened · a request waiting · released people with expiry dates · an expiry approaching · a release withdrawn · a subject removal request · concluded, ready to reopen). 12 sheets: conclude · custom · field · opening · people · photo · picture · release · reopen · script · share · tier. Subject prop: Sister (owner is not the subject) · Myself — every string forks on it through words().

The domain behind it, measured from biodata-core.js:

FactValue
Field registry60 fields in 9 groups (about 11 · work 6 · family 10 · community 4 · kundli 9 · expect 12 · talk 4 · contact 4, plus the custom group more)
Tiersbasic (anyone with the link) · released (approved one by one, dated, revocable) · private (never shared, structurally unreleasable)
RequiredfirstName · gender · age (derived from dob) · marital · city · education · profession · dob · phoneNo — the last two are private
Enum fields (product vocabulary, rendered per language)gender · marital · diet · bloodGroup · manglik · nakshatra · charan · gan · nadi · familyType · two expectation pickers
Peoplevalues.people[] of { id, relation, name, detail, seniority, order }, max 10; relations father · mother · brother · sister · mama · self; four family layouts (tree · vine · cards · list)
Photographsmax 4, ordered; the cover is the only one a forwardable link shows, served cropped; the rest wait for a release; no download control anywhere
Opening{ on, emblems: [] }, up to MAX_OPENING; emblem = image or words or both; scripts shipped: Devanagari, Latin
Custom fieldsmax 8, 6 types (text · long · number · date · yesno · items), 5 presentations, label ≤ 28 chars, conflictFor() refuses a label that repeats a standard field, up to 3 lead chips
AccessRELEASE_DAYS 90 · EXPIRY_WARN_DAYS 10 · REMOVAL_HOURS 72 · PLAN { subjectCap: 1, personLinkCap: 6 }
LifecycleCREATION_STATUS draft · live · ended, with life a sub-state of live: open · discussion · concluded
Languagesmr · hi · en; the UI is multilingual, the data is never translated (formatValue() may touch only ENUM_FIELDS)
Lift contract (the design's own storage hint)profile_field_visibility (profile_id, field_id, hidden) · profile_field_tier (profile_id, field_id, tier) · profile_photo (profile_id, id, position, is_cover, shown) · profile.theme_id

Implementation: none. No table, no RPC, no Edge Function, no seam, no schema, no screen. ⛔ on plan items 1.5–1.12 and 2.1–2.6, every one of which is still open.

1.5 Marriage profile, read in the app — BiodataView.dc.html ​

Props: viewer (A family you released · Anyone with the link · Owner preview) · screenState (Ready · Loading · Network error · Link withdrawn · Search concluded) · subject · familyLayout. Sheets: ask (a signed access request) · menu · share. The design's one rule: "WHAT A VIEWER MAY SEE IS DECIDED IN biodata-core, never in a screen" — shownFields(), visiblePhotos(), publishedCustom(). Implementation: none. ⛔ on 2.2 (the token-scoped public read).

1.6 The public page — setu-card/BiodataPage.dc.html (web) ​

9 scenarios: basic with one cropped photograph · basic text only · released with photographs · request submitted and pending · link expired · link withdrawn · concluded · removal requested (subject view) · loading; plus a WhatsApp link preview view that is photo-free by rule. noindex, no-store, in the owner namespace at /<slug>/biodata and /<slug>/biodata/<share>. Implementation: none. apps/web/src/app/routes.ts has no :slug/biodata route, and :slug itself is a 301 to the Setu Card for every owner kind — so a consumer's address currently redirects into a card route that will 404, which is the enumeration oracle decision D5 exists to close. ⛔ on 2.2 and Phase 5. ⚠ Without this page, every share control in the app hands out a dead link, so it is in the release build, not after it.

1.7 Account — Account.dc.html ​

ViewDesignImplementation
hubavatar + name + email · guest vs Confirmed account chip · Saved / Orders / Scans counters · Edit profile · four ACCOUNT_GROUPS (Personal: My QR setu · Personal details · Your areas · Your contact code — Your activity — Preferences — Support) · create-your-own-Setu-Card CTA · sign out · version🟡 six flat rows (no groups, no My QR setu row, no profile card, no counters, no sign-out); the guest banner is shown to every visitor including a signed-in one
detailsname · mobile · email with live validation · change photo · delete account behind a confirm❌ (setDisplayName and manage-account update_email / delete_account exist server-side)
areasthe areas you browse, current marked— absent with the marketplace off
notifschannels · topics · quiet hours; orders/payments locked ON with the reason✅ (built against the prefs seam)
appearancetheme · language · rupee note❌ (themeStore + localeStore already hold both)
privacywhat is on this phone · clear · download my data❌
helpaccordion FAQs · message support❌
aboutwordmark · positioning · version · legal❌
accountStateRegistered · Guest · Loading · Error🟡 loading only

⚠ The design persists preferences to qrsetu_consumer_prefs by merge, which the existing consumerPrefsService already does. The hub's Sign out must call the same useSignOut teardown the merchant uses (it clears the device PIN since 2026-09-03) — do not write a second sign-out.

1.8 Notifications — Notifications.dc.html ​

✅ Built as a derived list with Today / Earlier groups, read state, Mark all read, empty state. 🟡 The design now carries three biodata kinds (biodataRequest · biodataRemoval · biodataExpiry) and a Notification preferences link to Account?view=notifs; our CONSUMER_NOTIFICATION_KINDS has five kinds and none of the three. ⛔ The three read the biodata access rollup. ⚠ The design flags its own read state as a prototype shortcut: "On lift it must be PER ACCOUNT on the server: someone who reads a notification on their phone must not find it unread on the web an hour later." Ours is per device by deliberate seam design. Decision owed — QRS-1011.

1.9 Scan and verify — ScanVerify.dc.html ​

Design: camera view (always dark) · denied state · a QR setu code opens, it does not get reviewed (registry resolve → half-second label → push the owning screen) · wrong-audience sheet · the four-verdict sheet for anything not ours (verified · unknown · caution · danger, signals in plain words, payee card for UPI, business card, scope note) · report sheet with five reasons. Logic in qr-registry.js (a data table: order · item · order_receipt · biodata_share · biodata · identity · setu_card, biodata entries deliberately above setu_card) and qr-verify.js.

Implementation: apps/mobile/src/ui/scanner/ holds the whole camera shell (ScannerScreen, CodeScanner, ManualCodeEntry, useCameraAccess) built for both scan screens — and it has zero importers outside its own folder. There is no /consumer/scan route, and two of the four QR-tool entries in @qrsetu/domain point at it, so they are live dead links. The ledger already marks scan-verify missing. ⚠ The registry's identity matcher needs an owner-kind lookup (setOwnerKindLookup) that the design leaves unwired — "a person's identity slug still resolves as a Setu Card" (QRS-914). The slugs registry's owner_kind column is exactly that answer and now exists; it needs one RPC to expose it.

1.10 Everything else the design has, classified ​

ScreenStateClass
Chats (Chats.dc.html, rounds ≤ 22 built; 23 media + 24 message-level interaction not built)🟡keep as is for this release
OrderCode · ItemFeed · VendorFeed · ItemView · VendorView · Featured✅ builtnot reachable from Home with the marketplace off — keep, do not expand
MySessions (the Meetings tab's future destination: list · calendar · join · reminders)❌, no meetings schema anywhere in the repolater; reserve the 4th tab slot
MyProgress (the retention surface for the direct-seller vertical)❌later, not in the owner's five
Quick makers: LinkQR · WifiQR · UpiQR · PhotosQR (+ place · event · doc · social · profile in CONSUMER_QR_MAKERS)❌later; link / Wi-Fi / UPI are pure client (encode + PNG + share) and cheap once the chrome exists — see the UPI decision below
Intro card (§1.3 warning)❌later; its My QR setu row renders as not ready meanwhile, never as a dead link
Onboarding · PIN / app lock✅ in codedevice pass still owed (cases 53–66)

2 · Backend and dependency gaps that block the client ​

Measured on the repo, 2026-09-04. ✅ exists · ❌ absent.

#GapPlan itemStateBlocks
B1biodata_subjects · marriage_biodatas · shares · access requests · removal · RLS · one-active-per-subject partial index1.7❌ zero biodata references in any migrationHome stories/focus/progress/yourCards · MyQRSetu activated card · the whole editor · notifications' three kinds
B2family_members / people as jsonb objects, never a delimited string1.12❌editor family group, all four layouts
B3media_scope_exactly_one widened to three-way (owner_user_id)1.5❌ two-way today (20260814100000:73)photographs, avatar, own-picture emblems
B4private R2 bucket wired; manage-media stops hardcoding bucket: 'media' (index.ts:247)1.6❌every consumer photograph (a public bucket makes revocation impossible — D4)
B5feature_grants row capping subjects at 11.8❌the plan line on MyQRSetu, the create sheet's cap
B6profile reference: bigserial + Feistel in the EF, reference bigint unique1.10❌the number shown on every surface; irreversible after launch
B7biodata_templates sibling table + retire guard, template_key/version on the record, no FK1.11❌lossless template switching; irreversible after launch
B8pgTAP: a consumer reads nothing merchant-owned; an anon token read returns only its tier1.9❌release (CLAUDE.md requires it now that authenticated means the public)
B9manage-biodata Edge Function (create · edit · publish · release · withdraw · conclude · reopen · remove)2.1❌every write
B10token-scoped public read RPC2.2❌BiodataView · BiodataPage · the share links
B11rate limit on the anonymous read2.3❌ grep rate_limit over migrations and _shared returns nothingrelease (QRS-921)
B12subject notification over WhatsApp on publish (D3)2.4❌ (the channel exists: send-auth-otp's core)publish
B13Zod schemas + packages/data seam with a real impl bound in the barrel2.5❌every screen; build the seam first so screens start against the stub
B14cache purge on write2.6✅ invalidateCardCache pattern existsreuse
B15manage-account soft delete (users.status='deleted', keeps reclaim_hmac)1.4 second half❌ hard-deletes today (QRS-909)the account-deletion row in Account › details
NEW-1owner-kind lookup RPC over slugs.owner_kind (for qr-registry's identity matcher and the web /:slug greeting)not in the plan❌ScanVerify's identity route · D5
NEW-2/:slug byte-identical greeting for a person (D5) and /:slug/biodata[/<share>] routes in apps/webPhase 5 (5.1–5.3)❌ :slug is a 301 to the card for every ownerevery shared link
NEW-3the platform capability consumerMarketplacenot in the plan❌ no control existsfor this release a single domain constant, read through one function, defaulting false — the honest form of the availability axis for an unshipped capability; promoting it to a platform-control row is a later, separate decision
NEW-4notification read state per accountnot in the plandevice-local todaydecision QRS-1011
NEW-5expo-clipboard for the address copy controlnot in the plan❌owes the size callout before install (CLAUDE.md); the share sheet already offers Copy on both platforms, so it is a nicety with a cost

⚠ B6 and B7 are the two that are cheap today and expensive after the first real family publishes. The plan says so and it is still true: a reference format ends up on printed cards and in people's phones, and versioning retrofitted after templates exist is a migration.

The schema shape B1 needs — stated here so 1.7 is not designed twice ​

Two prior findings constrain it and they pull in opposite directions until you read them together: the build assessment's C2 applies ADR-0010's split rule (anything the platform FILTERS, SORTS, GROUPS, CHARTS, PRICES or GATES on is a typed column), and the design's own LIFT_CONTRACT hints at per-field tier and visibility rows. The registry has grown from ~40 to 60 fields across rounds 7, 8 and 9, adding fields in each round.

Recommended: typed columns for everything the platform gates or filters on — owner_user_id, subject_id, slug, status, life, reference, template_key, template_version, family_layout, content_language, published_at, concluded_at; and jsonb bags keyed by the registry's field ids for the content — values, field_tiers (overrides of the registry default), hidden, people, photos (ordered media ids with cover/shown), opening, custom_schema, custom_values. Nothing in the product filters on a field value, so the split rule is satisfied, and a design round that adds a field is a registry change, not a migration. The public RPC is the only place tier resolution may happen (C2's own caveat). Shares, requests and the removal are their own tables because they are gated and queried on. This is a recommendation for 1.7's migration header to argue against, not a decision — it is written down so the argument happens once.

3 · Release build versus later — the line ​

In the consumer release build:

Why it is in
Tab bar · centre button · More sheet · header (bell · clock · avatar)nothing else is reachable without it
Home identity hub (round 40, marketplace off): identity · stories · focus · progress · yourCards · createthe owner's item 1
My QR setuthe identity home the design routes everything through
Biodata editor + people and access, with sharethe owner's item 2
BiodataView (owner preview · recipient in the app)the design's "see it as a family sees it", and the in-app recipient
BiodataPage on the web, at /<slug>/biodatawithout it every share link is dead; the WhatsApp preview is photo-free by rule
Account: hub · details · appearance · notifs · privacy · help · about (areas absent)the owner's items 3 and 4
Notifications with the three biodata kindsalready built; the kinds are one derivation
ScanVerify, basic: registry resolve → open; verdicts for our own namespace; no reports and no code-status until those have a backend, and the sheet says sothe owner named scanning; the /consumer/scan dead link is a live defect
Subject notification over WhatsApp (D3)a publish that tells the subject nothing is a fiduciary gap
mr · hi · en for every new leafQRS-924 — Marathi-first, launch-blocking
Signed APK · privacy policy and terms · Sentry DSN6.3 · 6.4 · 6.5, all still open

Later, deliberately: the Intro card · quick makers · MySessions / Meetings tab · MyProgress · Chat rounds 23–24 · marketplace ON · per-recipient link analytics · push · today / weekRecap sections.

Sequenced by irreversibility first, then by what unblocks the most. W1–W2 are design-independent and run in parallel with W3–W4 (the plan's own thesis: screens compose against interfaces that exist and are tested).

WaveWorkUnblocks
W1 · Irreversibles1.4 soft delete · 1.5 media XOR · 1.6 private bucket · 1.7 + 1.12 schema (shape in §2) · 1.8 grant · 1.10 reference · 1.11 templates · 1.9 pgTAP · NEW-1 owner-kind RPCeverything
W2 · Backend surface2.5 schemas + seam first (client starts against the stub) · 2.1 manage-biodata · 2.2 public read · 2.3 rate limit · 2.4 WhatsApp · 2.6 purge · NEW-4 if decidedall writes and reads
W3 · Chromeconsumer _layout tab bar + centre button + More sheet + header · NEW-3 capability constant · route every existing consumer screenreachability of every screen
W4 · Homeidentity card completion (pulse · My QR setu · start block · copy if NEW-5 approved) · stories · focus carousel · progress · yourCards · create sheetthe owner's item 1
W5 · My QR setuall 7 states · share sheet · Available (locked) · Your identity rowsthe identity home
W6 · Biodata contentthe editor: 9 groups · 60 fields · people sheet · photos · opening · custom fields · 10 statesthe owner's item 2
W7 · Biodata peopleshares · requests · release with expiry · withdraw · removal · conclude / reopen · celebration · 8 statesaccess
W8 · BiodataView3 viewers · 5 states · ask · sharepreview and in-app recipient
W9 · Accountprofile card · groups · details · appearance · privacy · help · about · sign out via useSignOutitems 3 and 4
W10 · Notifications + Scanthree biodata kinds · /consumer/scan on the existing scanner shell · registry as a domain module · basic verdictsthe dead links
W11 · Web/:slug/biodata[/<share>] · /:slug consumer greeting (D5) · headers export (noindex · no-store · Cache-Tag) · photo-free OGevery shared link
W12 · Shipi18n leaves in three languages (Marathi human-reviewed) · parity contracts with reachableFrom on every new route · device suite cases · signed APK · privacy/terms · Sentry · a fresh iOS buildrelease

⚠ Every screen in W4–W10 gets a parity contract enumerated from the design's own states before it is called done (the fourth rule), and every route-rendered scenario carries reachableFrom — the rule added on 2026-09-03 because a green contract covered an unreachable PIN gate for a month.

5 · Decisions owed before the wave that needs them ​

#DecisionNeeded byRecommendation
D-aMarketplace OFF for the consumer releaseW3Yes. It is the design's default and the owner's brief. Saved and Orders render the design's honest empty states ("Saving shops arrives with the marketplace")
D-bThe 4th tab while Orders → Meetings is pendingW3Keep the bar 2 + centre + 2 with Orders showing its honest empty state, and put no new code behind it; swap the destination to MySessions when the meetings schema exists
D-cNotification read state: per device (ours) vs per account (the design, "on lift") — QRS-1011W2Per account; one small table, and the web will otherwise disagree with the phone
D-dPersonal UPI QR maker (CONSUMER_QR_MAKERS.upi) vs the standing rule no UPI promotion, no vendor UPI ID — QRS-1009later (not in this release)The rule was written about vendor payment; a person's own UPI code is a different thing. Decide it, do not let it slide in with the makers
D-ePDF export: decisions §6 records "a deliberately limited PDF export exists" (round 1); the design's share sheet now says "No export, and it is stated. No PDF and no image" — QRS-1010W7Follow the later design (no export); it is the stronger privacy posture and the share sheet already states it
D-fBiodata URL: /<slug>/biodata (decisions §4 left it open; the design has since built on it, and biodata is a reserved slug)W1Confirm the design's answer
D-gMinimum age (decisions §4)W6Owner decision; the dobRange in biodata-core carries an AGE_FLOOR
D-hModeration of an uploaded emblem or photograph — post-report, remove the emblem never the profileW6As recommended in the readiness log (B3)
D-iexpo-clipboard for the copy control (NEW-5)W4Owner's size call; the share sheet already offers Copy

6 · What this reconciliation cannot see ​

  • Whether the enumeration is complete. It is a human read of one design pull and one repo, and it finds what it looked at. The parity contracts written in W4–W10 are the falsifiable form.
  • Visual fidelity. Nothing here judges spacing, hierarchy or whether a family will trust the page.
  • The device. The auth and app-lock work from 2026-09-03 is proven by 1,101 unit tests and by nothing on hardware; the same will be true of every screen here until the device suite runs.
  • Production. Every measurement is against the repo and Dev.

No code was changed for this reconciliation, and no merchant or enterprise file was read for modification.