Implements S12-decision-sheet.md verbatim (✅ rulings designed; 🚫/⏳ absent; 🔨 cited in captions + foot). Law #11 governs everything here: the profile shell — cover, avatar + OG-CREATOR ring, follower counts, action pill, name row, @handle | Public, joined token, scrollable tabs — is the REAL app profile from master-plan/refs/real-profile-top.png, redrawn 1:1 with ZERO shell surgery. Civic slots IN: B5 badge row (in the shell's existing chip slots), A1 "From Bheldi" in the existing token row, B6 Local Seva card beside the existing Nyburs Rank card, ONE appended Civic tab. Cover + avatar are gradient stand-ins only because v2 carries no external imagery — in app they are the user's own media, untouched. DECISIONS #10: Seva, never "karma" (🔴 the live widget still says LOCAL KARMA).
Law #11: shell = refs/real-profile-top.png 1:1 — cover, back chevron, OG-CREATOR avatar ring, "27 Followers | 13 Following", action pill, name + verified check, @navincse | Public, token row, scrollable tabs. Civic additions only: B5 badge row consolidates the shell's two existing Steward-chip slots, in ruling order — gold Official → dated "★ Top Voice · Jun 2026 · Bheldi" (THE screenshot surface, B9 tap → S10 share card) → Founding Steward → role chip "संरक्षक · Bheldi" (place name required; role display names are per-locale, DECISIONS #10) · 🚫 NO Contributor chip (zero signal) · A1 "From Bheldi" = pin + chevron in the EXISTING token row, tappable → place page, default ON post-HOME_CONFIRMED · B6 LOCAL SEVA card beside the existing rank card — 🔴 P0: the LIVE card renders "LOCAL KARMA" + "N karma" (E14 sweep: profile_civic.widget.dart:352/807/909 + i18n local_karma/karma_points ×12 locales); big number = civicScore (already the cross-place sum — S10 B7, no new endpoint), "1,240 Seva · 3 places", subtitle top place + hi role · B8 weight not removal — follower counts STAY · Civic = ONE appended tab. 🔨 A1 homePlaceName absent from the profile fragment (resolve homePlaceId→name, S) · B5 badges[] MISSING from PlaceKarmaOutput DTO (additive S) · E15 civic fields uncached today (2–3 queries/profile load) — Redis users#{id}#civic EX3600, invalidate on grant.
The DECISIONS #4 aggregation tab. B7 Place Standing cards — place + hi role + Seva, and the level-ladder tease lives HERE, never the header: "next: अगुआ — 220 Seva to go" + locked segment bar · C11 Issues Reported rows carry INLINE status chips — the RESOLVED row is the conversion proof moment (status track + push already locked, prior decisions) · Contributions show ALL states to self — accepted ★ +12 Seva / pending / rejected (strangers never see pending/rejected, see S12·C) · C12 the "Posts from Bheldi" filter chip SHIPS on the existing Posts tab — #27 / 🔨 XS: getUserPosts already accepts locationId; index note: conditional hint if it becomes paginated first-class. 🔨 E15: the Civic-tab REST call needs a TIMEOUT (2G hang) and the civic posts section renders shimmer, never a full-page spinner; Issues-Reported reads posts (civicOnly) — a deleted post hides an open issue (semantic gap, documented); latent bug: GetUserCivicPosts userId nullable fallback; compound {contributorId,createdAt} index before steward-volume use.
C10 PUBLIC v1, the deferred decision settled by the data lens: the server ALREADY strips the Seva score for other users (getCivicPublicBadges returns role/stewardOf/topKarmaRole only — a deliberate posture, 🚫 removing the LocalKarmaCard other-user guard) — so NO Seva card on this shell; "witnessed status" is served by the LEGIBLE parts: office banner, role chip with place, dated Top Voice stack, "From Bheldi" line (rendered per the A3 ONE-control coupling — placeListVisible OFF hides her from the People tab AND hides this line; self-view always shows). Revisit score exposure with the conversion metric (civic-tab → follow ≥12% by D30). C11 Civic tab VISIBLE because she holds Seva/office; content = Place Standing (role + place, NO raw number) + Issues filed (already-public posts) WITH status chips — the RESOLVED row is the follow-conversion proof — + contributions ACCEPTED-ONLY (🔨 status filter on the public endpoint — today it returns ALL incl. rejected; hide pending/rejected from strangers) · mini-state below: a zero-Seva stranger gets NO Civic tab at all (🚫 stranger-facing empty states). 🔨 C11 GetUserCivicPosts is SELF-ONLY today (userId from own DB) — extend for other-user (S).
D13 ONE "Place privacy" section inside Settings → Privacy (never a Places-specific flow), three rows: (a) "Show me on Bheldi's People page" — the A3 ONE control couples roster + identity line: OFF hides you from the People tab AND hides "From Bheldi" on your public profile (self-view always shows); 🚫 separate toggles · (b) info line names THE place — "You appear on Bheldi's page" (primaryLocation = ONE home chain; 🚫 "N place pages" — wrong mental model) · (c) A2 "Also show where I live now" = HARD OPT-IN offered at an obvious moment (profile edit), STATE granularity stored even when the city is asked, never auto-populated; + manage-home link. Outcome previews: A2 migrant dual line "From Bheldi · in Delhi"; A4 composite moment — current-month Top Voice of your home place renders "From Bheldi · #1 this month" (same token, dynamic). hi labels per the rural lens ("दिखें गाँव के लोगों में"). 🔨 BOTH fields ABSENT today: placeListVisible (add + People-tab filter predicate — the S9 launch-blocker rides here) and settings.currentCityOptIn (new sparse field + mutation, M-lite; usersIdx verified safe — settings subfields not indexed).
DECISIONS #8: ONE language per screen via i18n — this frame renders S12·A end-to-end in hi. 🔴 E14 P0: the hi locale file ships ENGLISH "Local Karma" today — a Hindi-set phone sees an untranslated English violation of DECISIONS #10; sweep = profile_civic.widget.dart:352/807/909 + leaderboard hardcoded :515/524/530 + i18n keys local_karma/karma_points across ALL 12 locale files. Here: स्थानीय सेवा card ("1,240 सेवा · 3 जगहें"), badge row आधिकारिक / टॉप वॉइस · जून 2026 · भेल्दी / संस्थापक संरक्षक / संरक्षक · भेल्दी, identity token "भेल्दी से", tabs पोस्ट्स/जवाब/लेख/मीडिया/नागरिक. Numerals, @handles and the brand "Nyburs" stay Latin. Role display names ship as their own per-locale task (DECISIONS #10). Layout, slots and ordering identical to S12·A — the shell itself is already locale-aware in app.
Journey position: S12 = the DECISIONS #4 profile integration — reuse + merge + improve the EXISTING profile, plus ONE tab aggregating all place activity. Law #11 is the screen's spine: the shell (cover, OG-CREATOR avatar ring, counts row, action pill, name + verified check, @handle | Public, token row, scrollable tabs) is refs/real-profile-top.png redrawn 1:1 — civic elements only OCCUPY existing slots; the badge row consolidates the shell's two existing Steward-chip slots per B5. App-only / build-phase (cited in captions, not designed elements): A1 homePlaceName missing from the profile fragment — resolve homePlaceId→name (S) · A2 settings.currentCityOptIn field ABSENT — new sparse field + mutation (M-lite; usersIdx verified safe, settings subfields not indexed) · A3 placeListVisible field ABSENT — add + People-tab filter predicate (the S9 launch-blocker rides here) · B5 badges[] MISSING from PlaceKarmaOutput DTO — additive (S); Top Voice = NEW chip type, badge data exists per S10 A3 · B6 civicScore already equals the cross-place sum (S10 B7 — no new endpoint) · B9 badge tap → S10 share card pre-filled (month/place), share sheet primary, leaderboard nav secondary · C10 server posture STANDS — getCivicPublicBadges strips the score for other users; revisit with the conversion metric · C11 public contributions need an ACCEPTED-only status filter (today returns all incl. rejected); GetUserCivicPosts is self-only today (userId from own DB) — extend for other-user (S) · C12/#27 "Posts from {place}" chip = XS (getUserPosts already accepts locationId; conditional index hint if paginated first-class). 🔴 E14 P0 karma→Seva sweep (exact locations from the data lens): profile_civic.widget.dart:352/807/909 + leaderboard hardcoded strings :515/524/530 + i18n keys local_karma/karma_points across ALL 12 locale files — hi currently ships ENGLISH values. E15 perf: civic fields uncached (2–3 queries per profile load) → Redis users#{id}#civic EX3600, invalidate on grant; compound {contributorId,createdAt} index before steward-volume use; Issues-Reported reads posts (civicOnly) — a deleted post hides an open issue (semantic gap, documented); Civic-tab REST call needs a timeout (2G hang) + shimmer never full-page spinner; latent bug: GetUserCivicPosts userId nullable fallback. F events: profile_civic_badge_row_seen · badge_tapped{type,isOwn} · seva_card_seen · civic_tab_opened{isOwn} · identity_line_tapped · share_card_opened/shared · follow_from_civic_tab · place_filter_applied · place_privacy_toggle{field}. METRIC: civic-tab → follow conversion on other-user profiles ≥12% by D30 (<8% = self-serving log; >15% = promote the surface).