Appearance
Ganapati merchant journey — design ⇄ implementation parity audit
Started 2026-08-14 against design round 22. The owner reported "significant UI/UX and functionality drift in the localhost build… This level of deviation is not acceptable" and asked, twice, for every mismatch, missing interaction, stale component and incomplete feature to be identified. This page is that enumeration.
THE DEFECT THIS PAGE EXISTS TO CORRECT IS A CATEGORY ERROR, NOT A LIST OF BUGS
npm run check:screens reports 27 of 36 built and is green. That number is PRESENCE — does an implementation path exist for an approved design. It was allowed to imply PARITY — does the implementation match the design. It never did, and the ledger says so on its own face.
So the honest statement of where the merchant app stands is two numbers, not one:
| Approved merchant screens with an implementation | 16 of 16 reachable (check:screens) |
| Approved merchant screens verified to match the design | 0, before this audit |
CLAUDE.md's third rule names exactly this: "a conclusion was asserted where an enumeration was owed."
What this audit is, and what it cannot be
- It is a structural comparison. For each screen: pull the round-22 design, enumerate its sections, controls, states and copy, and check each against the built screen.
- It is NOT a fidelity check. No amount of reading catches wrong spacing, a bad transition, or a header two pixels off. That stays design-pull-then-implement plus a drift-ledger row (ADR-0015).
- It is NOT complete yet. Six screens are audited in depth below; nine are not. Every unaudited screen is marked as such. Completion of parts never bounds the whole — the same rule that made QRS-626 necessary.
Status by journey step
The 20 steps are vendor-core.JOURNEY, as recorded in the design project's own VENDOR-PARITY.md.
| # | Step | Design file | Audit | Verdict |
|---|---|---|---|---|
| 1 | Onboarding | onboarding/Onboarding.dc.html | ⏳ not audited | — |
| 1b | Welcome / brand moment | onboarding/BrandSplash.dc.html | ⏳ not audited | — |
| 2 | Business profile | mobile-console/Profile.dc.html | ✅ audited | 🔴 5 sections missing |
| 3 | Setu Card (public) | setu-card/SetuCard.dc.html | n/a — Stack 1 (apps/web) | — |
| 4 | Card editor | mobile-console/CardEditor.dc.html | ⏳ not audited | built 2026-08-13 from design |
| 5-7 | Store / Catalogue / inventory | mobile-console/Catalogue.dc.html | ✅ audited | 🔴 the largest gap in the app |
| 8 | Dashboard | mobile-console/Home.dc.html | ✅ re-audited against a FRESH pull 2026-08-14 | 🟢 structural parity; 4 data sources unwired, 3 undesigned components pending an owner call |
| 9 | Orders / Collections | mobile-console/Collections.dc.html | ✅ audited | 🟡 close — 2 gaps |
| 10 | Bookings | no screen either side | — | consistent |
| 11 | Order detail | mobile-console/OrderDetail.dc.html | ⏳ not audited | built 2026-08-13 from design |
| 12 | Payments | mobile-console/Payments.dc.html | ✅ audited | 🟢 closest match in the app |
| 13 | Chat | Messages.dc.html + Thread.dc.html | ⏳ superseded by rounds 23/24 | media + actions adopted; client outstanding |
| 14 | Notifications | mobile-console/Notifications.dc.html | ⏳ not audited | — |
| 15 | Reminders | no design (shipped undesigned, owner decision) | — | consistent |
| 16 | QR tools | mobile-console/QRTools.dc.html | ⏳ not audited | — |
| 17 | Settings | mobile-console/Settings.dc.html | ⏳ not audited | round-17/20 sections added 2026-08-13 |
| 18 | Banking and payouts | mobile-console/Banking.dc.html | ⏳ not audited | built 2026-08-13 from design |
| 19 | Plan and billing | mobile-console/PlanBilling.dc.html | ⏳ not audited | built 2026-08-13 from design |
| 20 | Card and QR activity | mobile-console/Analytics.dc.html | ⏳ not audited | built 2026-08-13 from design |
| — | Scan to collect | mobile-console/ScanCollect.dc.html | not built | camera seam ready (QRS-645) |
| — | More (directory) | mobile-console/More.dc.html | ✅ audited | 🟡 4 gaps, one from misreading round 22 |
⚠ The pattern in the "not audited" column is worth reading before assuming the worst. The six screens built on 2026-08-13 came straight off a fresh design pull, so they are the ones least likely to have drifted. The screens that drifted are the older ones — Home, Store, Profile, More — built before the design settled and never re-pulled. That is the actual mechanism of the drift the owner is seeing: not sloppiness, but a design that kept moving while the implementation stood still.
Step 8 · Dashboard — 🔴 structurally a different screen
This is the biggest single finding, and it is on the first screen a merchant sees.
Design round 22 (Home.dc.html) | Built (DashboardHome) |
|---|---|
Workspace header: initials + business name + label · area + bell + owner initials | ✅ HomeHeader |
| Season context banner — permanent chrome, dismissible, "Ganesh Chaturthi in 12 days · 34 of 60 listings available" | 🔴 absent |
| Offline banner — "Offline. Showing what was saved at 8:52 am." | 🔴 absent |
| Greeting + sub | ✅ |
| Quick actions — Scan & collect · Orders (badge) · Collections (badge) · Payments, capability-gated | 🔴 removed by me in QRS-582 — see below |
Share-card strip, with card status live / not published / needs attention | 🟡 HeroCard — similar role, unverified against the design's shape |
| Compact KPI strip — four numbers, one row, ~80px, each one a link | 🟡 MetricPanel — four numbers, not linked, and carries a rating tile the design deleted |
Needs attention — swipeable rail sorted by SEVERITY, grouped (three missing photos = one card), per-card dismiss, View all → Notifications, and instant actions that advance an order in place | 🔴 absent entirely |
| First run — numbered next steps instead of zeros, + "Your dashboard fills itself in as you add items and take orders." | 🔴 absent |
Dashboard groups — collapsible, resolved from capabilities, each with a summary line; grid widgets declare a span, list widgets become rails. Widget kinds: stats · bar (two denominators) · chips · bars-chart (collections by day) · spark · ring · activity · slot · undesigned, each with its own skeleton and a failed + Retry state | 🔴 absent entirely — the app has one reminders slot, a live-orders card and a plan nudge |
| Tab bar · centre sheet · toast | ✅ |
| No plan nudge, no full-width live-orders card, no rating | 🔴 all three present in the build |
The two corrections I owe on this screen
- QRS-582 removed Quick Actions citing the design, and round 22 has them. The commit's own reasoning was "the approved round-19 Home contains neither. Verified against
prototype/mobile-console/Home.dc.html: zero matches for 'quick action'." That grep was true when it was run and is false now: round 22 re-added quick actions with real destinations (Scan & collect, Orders, Collections, Payments) rather than the placeholder routes that justified deleting them. ⚠ A verified claim about a moving document has a shelf life, and nothing in the repo measures it — this is QRS-650's lesson arriving a second time, in the opposite direction. - The sponsored-strip removal was correct and stays removed. Round 22 §3.2 removed the sponsored banner from desktop too, for the same reason. Only the quick-actions half of QRS-582 was wrong.
What the dashboard needs, stated as work rather than as a complaint
The design holds no industry knowledge — it resolves a widget list from vendor-core and lays out whatever comes back. So the build is not "add nine widgets"; it is one widget-resolution contract in @qrsetu/domain plus a renderer per widget kind, which is exactly the shape ADR-0021 D4 demands (ask for a feature, never branch on archetype). Building it screen-first would reproduce the conditional sprawl the platform model exists to prevent.
Steps 5-7 · Store / Catalogue — 🔴 the largest gap in the app
The design is a full inventory manager. The build is a list with a four-field composer.
Present in the design and absent from the build:
- Search with a clear button, and a distinct "No items match" state separate from "No items yet"
- Category chips + a Manage categories sheet — reorder (up/down), rename, delete, add
- Availability filter with selection state
- Per-item: image slot or emoji tile · Duplicate (a stall carries a hundred pieces differing by one measurement) · compare-at price + derived discount % · attribute line + "attributes missing" state · tappable badge · stock note · pause
- Quick add sheet: photo toggle + "Take the photo" · per-industry attributes · hints · "Just added" list with Undo · "Save and next" (partially built — the loop exists)
- Full form sheet: image slots · emoji toggle · name with a duplicate-name explanation ("That is fine for a second identical piece, rename it if this one is different") · price with the "leave at 0 to show Price on enquiry" hint · "What buyers filter by" attributes · a More options disclosure holding Type · Unit · Category · Description · Highlight tag · Was price · Tax (GST) + tax-inclusive · Availability · Variants (with per-variant stock) · unique-vs-countable · Track inventory + stock quantity ± stepper
- Clone mode: "Reuse the original's photos" and "Add and duplicate again"
- Delete confirm that warns when an order references the item, with a hand-off to settle it
Already known and tracked: three of these — per-item photo, facet values, reorder — are QRS-652, built-and-deliberately-unwired because they have nowhere to be stored. Photo additionally needs object storage, which is QRS-657.
Step 2 · Business profile — 🔴 five sections missing
| Design | Built |
|---|---|
| Avatar + camera button · name · tier pill linking to Plan & billing · email row | 🟡 avatar ✅, tier pill absent |
"YOUR CARD ADDRESS" card — qrsetu.com / <slug>/setu-card, copy, share, caption "This address is on your printed QR code, so it never changes.", and a View link to the live card | 🔴 absent entirely |
| Tabs Basic · Business · Hours · Social (business) / Basic · Location · Social (individual) | ✅ |
| Basic: owner name · mobile (+91, 10 digits) · email read-only + Change → Settings + "Managed in Settings, Security." | ✅ |
| Business: brand name · industry as a READ-ONLY row + "Set at signup, because it decides which features your business gets" + a sheet explaining write-once with Contact support · GSTIN + 15-char validation | 🔴 industry is a live PillSelect — which is also the archetype-immutability defect (S-D4 in the plan) |
| Hours: Timezone card ("Open and closed is read here") | 🔴 absent |
| Hours: 7 day rows with from/to pills, Closed, toggle | ✅ |
| Hours: Outside hours reply — switch + message textarea | 🔴 absent — and round 22 §3.5 makes this load-bearing: the phone carries it in both Profile and Messages, so a merchant who switches it on at the counter can find it at the desk. Ours has it in Messages only. |
| Hours: planned holidays + add + remove + empty state | ✅ |
| Social: 8 links, Google Business first, brand glyphs in their own colours (a deliberate token exception), check when filled | ✅ |
More (directory) — 🟡 four gaps
| Design | Built |
|---|---|
| Grow group FIRST, then Manage | 🔴 inverted — Manage first |
| Both groups are a 2-column tile grid (40px icon tile, label, sub) | 🔴 rendered as list rows (Card variant="list" + SettingRow) |
| Grow: 5 unlinked tiles with a "Not backed yet" pill, dashed border | 🟡 present as a group; shape differs |
| Manage: 8 capability-gated entries | 🟡 all 8 present, plus two the design does not have — a Banking row (the design reaches banking through Settings) and a separate Plan card |
| "The same console on a computer" — dashed link into the desktop console, "Every screen here, wider: multi select, keyboard shortcuts and the order open beside the list." | 🔴 absent, and this one is my misreading. Round 22 §3.8 says the old "Also on desktop" block was replaced with one honest link. My implementation deleted it and documented the deletion as the design's decision. Replaced ≠ removed. |
| Workspace header as chrome above the title | 🟡 built as an identity card below the title |
Step 9 · Collections — 🟡 two gaps
Day grouping, per-day summaries, order cards with buyer/item/phone, the three chips, call and chat buttons, the three collect-button states and the balance-confirm sheet all match. Missing:
- The scan button in the header (→ ScanCollect). A nav affordance, blocked by the scan screen.
- The offline banner — "You are offline. Marking a collection will send when you are back." This is QRS-649: the app has no offline detection on native at all, so every offline affordance in every design is unbuildable until NetInfo lands. The same banner is specified on Home and Payments, so one native module closes three gaps.
Step 12 · Payments — 🟢 the closest match in the app
Built 2026-08-13 directly from the round-20 design and it shows: the single period control, the flow-vs-position separation, the taken card with its parts, "Still to collect" handing over to Collections, the method split with shares, the banking prompt, the counter-sale sheet with its three fields and its typed-then-cleared error rule, the day-grouped ledger, and the footnote all correspond. Only the offline banner is missing (QRS-649, as above).
This is the useful control in the experiment: a screen built from a fresh pull, by the same process, on the same day, is in near-parity. The drift is not a capability problem — it is a re-pull cadence problem.
Closed so far (2026-08-14) — kept on the page rather than deleted
A closed gap is evidence the audit worked; deleting the row makes the page look like it never found anything.
| Screen | Gap | Closed by |
|---|---|---|
| Home | Quick actions absent (QRS-582 deleted them citing a round-19 grep) | Restored with real destinations. Badges derive in @qrsetu/domain from the one order-book query Collections reads. |
| Home | A rating tile the design deleted in round 19 — an invented 4.8 with a fabricated -0.1 trend | Removed from the packages/data stub. Its absence is asserted by name, because the tile count is what let it survive. |
| Home | KPI cells were read-outs; the design's strip is links | StatTile.href, optional per metric so a number with no screen behind it stays plain text. |
| More | Manage group first | Grow first, per the design. |
| More | List rows instead of a 2-column tile grid | DirectoryTile, two variants (linked · unbacked-and-inert). |
| More | The desktop entry, missing because I read round 22's replaced as removed | Restored, as an informational card — ADR-0011 leaves no desktop console to link to. Drift row recorded. |
⚠ One defect was introduced and caught by driving the built export, which is worth more than the fix. The Collections tile was first wrapped in if (today), so before the order book resolved the row showed two tiles and then silently became three — a control set changing shape under the merchant's thumb. Type-check, lint and 33 jest cases were all green. Presence is decided by capability; the badge is decided by data. Conflating them is invisible to every static gate, and .claude/skills/run-mobile/driver.mjs found it in one pass, which is the same argument QRS-630 makes for a journey walk.
Closed 2026-08-14, second tranche (QRS-660): the whole widget system — registry, resolution, payload switch, groups, rails, per-kind skeletons, failed-and-retry — plus the season banner, the needs-attention rail with its in-place order action, and first-run steps. Built in @qrsetu/domain as a resolution contract, so a widget is a registry entry rather than a component and the desktop console inherits it.
Still open on these two screens: five dashboard DATA SOURCES (season, counter sales, chat summary, the card/QR rollup, and the reserved/sold split — QRS-661), each of which makes its own widget disappear rather than print a zero; the offline banner (QRS-649); More's workspace-header chrome; and More's two extra Manage tiles, which wait on the Profile tier pill or Plan and billing becomes unreachable.
The three systemic causes, so the fix is not just a to-do list
- No gate can see fidelity, and the one gate that exists reports presence.
check:screensis green while six audited screens diverge. The ledger states its own limit; the limit was not carried into how the number was reported. Contract files per screen (QRS-631) are the designed answer and remain unbuilt. - A verified claim about the design has a shelf life, and nothing measures it. QRS-582's grep was correct in round 19 and wrong by round 22. QRS-650 proposed
npm run screens:sync— a command, not a gate, because a gate that needs the network gets switched off — plus recording the design round number in the ledger. Both still unbuilt. - Three design rounds were adopted while the drift went untouched. Camera, then audio/media, then message actions. Each adoption was correct in isolation; the ordering was mine, and I ordered by dependency while the owner was asking for parity. Recorded in delivery-log #39.
Sequencing recommendation
Journey order, backend-independent first. Nothing in the merchant gap list above is blocked on an Edge Function; the two exceptions are named (offline needs NetInfo, per-item photos need object storage).
- Dashboard — the widget-resolution contract in
@qrsetu/domain, then the widget kinds, then season context, needs-attention, first-run, and the restored quick actions. - Store — search, categories, the full form sheet, duplicate.
- Profile — card address, timezone, away reply, industry read-only + sheet, tier pill.
- More — group order, tile grid, the desktop link, drop the two extra rows.
- NetInfo (QRS-649) — closes the offline banner on Home, Collections and Payments in one change.
- Audit the remaining nine screens and extend this page. Until then, this page's own coverage line is the only honest summary: 6 of 15 audited.
Living doc: extend it in the same change that closes a gap, and move the row rather than deleting it — a closed gap is evidence that the audit worked, and deleting it makes the page look like it never found anything.