Catalog — requirements
Epic. MVP-1 requirements breakdown for the Catalog browse surface (see the feature architecture). Each FR is sized to become one or a few user stories. The SDK adapter layer is already built (
SupabaseCatalogAdapter) — this epic covers the bloc, page, and flow-test layer.
| Type | Functional |
| Layer (build approach) | Frontend — design_system atoms + app bloc/page + flow-test |
| RICE | R 9 × I 2 × C 80% / E 2.5 = 5.8 · Tier MVP-1 |
| KPI (summary) | Browse→action conversion |
| Traces to | feature catalog · C4 components |
| Depends on | Money (redeem) · Admin (CRUD) · Today |
Success criteria (definitive KPI)
Success = ≥ 40% of Catalog sessions end in an action (claim, redeem, or open editor) within the same session, sustained across weeks 2–4 after activation.
- Metric: sessions with ≥ 1
chore_claimed,reward_redeemed, orcatalog_editor_opened/ totalcatalog_viewedsessions. - Target: ≥ 40% conversion rate.
- Window: sustained across weeks 2–4 post-activation (activation = household's first approved chore completion).
- Why this bar: the Catalog's job is to move a member toward an act; a browse surface that doesn't convert is furniture. 40% reflects that some sessions are deliberate "shopping" that may span multiple visits.
- Guardrail (catalog depth): if conversion rises but median catalog items per active household stays ≤ 2, high conversion is forced selection (shallow catalog), not genuine engagement. Require median ≥ 4 catalog items per active household before treating conversion as healthy.
- Baseline: no install base yet — treat 40% / ≥ 4 items as the launch hypothesis; re-baseline from first-cohort wk-2 data.
Analytics — events to record
All events are internal BI — event-type + coarse properties, no child identity or PII, content-free. Never routed to marketing; never used to target minors. Emitted through the consent-gated analytics substrate.
| Event | When | Key properties | Priority | Feeds |
|---|---|---|---|---|
catalog_viewed | Catalog screen opened | tab (chores|bounties|activities|rewards), itemCount (coarse bucket) | P1-with-surface | KPI denominator |
chore_claimed | Bounty claimed from Catalog | choreKind (bounty), memberRef (opaque) | P1-with-surface | primary KPI numerator |
reward_redeemed | Reward redeemed | tokenCost (coarse bucket), memberRef (opaque) | P1-with-surface | primary KPI numerator |
catalog_editor_opened | Chore editor entry point tapped | surface (catalog), choreKind (chore|bounty|activity|reward) | P1-with-surface | primary KPI numerator · admin funnel |
All four events ship with the surface — the KPI is only measurable once both the denominator (
catalog_viewed) and the three numerator events are instrumented.
Scope
Catalog is the browse IA quadrant (act / glance / browse / people). It shows everything available to household members: assigned chores, claimable bounties, unlockable activities, and redeemable rewards. Members browse and act (claim, redeem); Admin CRUD lives in the Admin hub. The chore editor route already exists — the Catalog surfaces its entry point for parental members only. MVP-1 delivers the tabbed browse IA, bounty claiming, reward redemption, activity lock visibility, empty states, and the editor entry point.
Functional requirements
FR-CATALOG-1 — Tabbed browse IA (chores, bounties, activities, rewards)
Priority: P1 · Status: 🔨 to build (SDK-ready) Statement. As a member, I open the Catalog and see four tabs — Chores, Bounties, Activities, and Rewards — so I can browse what is available to me in one place. Acceptance
- Given a household with items in each category When I open Catalog Then four tabs render and each shows household-scoped items.
- Given I switch tabs Then
catalog_viewedfires with the correcttabvalue. - Given a tab has no eligible items Then the empty state renders (FR-CATALOG-7), never a blank screen.
FR-CATALOG-2 — Eligibility filtering per member
Priority: P1 · Status: 🔨 to build Statement. As a member, I only see chores and bounties I am eligible to act on — ineligible items are hidden by default. Acceptance
- Given a chore with
minAgeabove my age When Catalog loads Then it does not appear in my Chores tab. - Given a bounty with no
assignedMemberIdsrestriction When any age-eligible member opens Catalog Then it appears for them. - Eligibility resolution is delegated to the SDK service layer; the bloc passes
memberId; no eligibility logic in the page widget.
FR-CATALOG-3 — Claim a bounty
Priority: P1 · Status: 🔨 to build (BountyRow widget reusable from Today surface) Statement. As a member, I tap "Claim" on a bounty and it enters my Today list for the day, so I can act on it without an admin assigning it to me. Acceptance
- Given a claimable bounty When I tap Claim Then it appears in my Today list and the button reflects a claimed state.
- Given a bounty's
maxPerDaycap is reached Then Claim is disabled with a clear affordance. - Given claim succeeds Then
chore_claimedfires withchoreKind = bountyand an opaquememberRef. - The claim call goes through
CatalogBloc → Repository → client_sdkfacade; no direct adapter call in the page.
FR-CATALOG-4 — Redeem a reward
Priority: P1 · Status: 🔨 to build Statement. As a member, I tap "Redeem" on a reward and the token cost is debited from my balance, so I can spend earned tokens on household rewards. Acceptance
- Given my balance ≥ reward cost When I tap Redeem and confirm Then tokens are debited via
EconomyServiceand the reward shows a confirmed state. - Given my balance < reward cost Then Redeem is disabled with a balance indicator.
- Given redemption succeeds Then
reward_redeemedfires with coarse-bucketedtokenCostand opaquememberRef. - A single tap-and-confirm pattern prevents accidental redemption; no undo in MVP-1 (append-only ledger).
FR-CATALOG-5 — Chore editor entry point (parental members only)
Priority: P2 · Status: 🔨 to build (editor route already exists from P1/P2 build) Statement. As a parental member, I tap an edit affordance on a catalog item and the existing chore editor opens pre-populated, so I can adjust items without leaving the browse context. Acceptance
- Given I am a parental member When I tap the edit affordance on any catalog item Then the existing chore editor route opens pre-populated with that item.
- Given I am a non-parental member Then no edit affordance is visible; items are read-only.
- Given the editor opens Then
catalog_editor_openedfires withsurface = catalogand the item'schoreKind.
FR-CATALOG-6 — Activity lock state visible
Priority: P2 · Status: 🔨 to build Statement. As a member, locked activities show a lock indicator and a one-line unlock condition so I understand what is required to access them. Acceptance
- Given an activity with an unmet
ActivityGateWhen I view the Activities tab Then the item shows a lock icon and a coarse gate label. - Given the gate is met When the tab refreshes Then the lock clears without a restart.
- Tapping a locked activity renders an informational sheet or toast; it does not crash or navigate.
FR-CATALOG-7 — Empty states per tab
Priority: P2 · Status: 🔨 to build (DS empty-state atom available) Statement. As a member, an empty tab shows a purposeful message rather than a blank screen so I understand what the section is for. Acceptance
- Given a tab with no eligible items for me When it renders Then an empty state with copy reflecting "nothing for you yet" displays.
- Given a parental member views an empty tab Then the empty state includes a CTA into the Admin catalog editor.
- DS empty-state atom is reused; no new illustration asset required for MVP-1.
Architecture considerations
- One data path —
CatalogBloc → Repository → client_sdkfacade; no directdrift/supabaseimports in page or bloc.SupabaseCatalogAdapteris already built. - Read via repository → facade —
CatalogServicemediates all reads; the bloc calls the repository, never the adapter. - Household-scoped RLS — all catalog tables are RLS-gated by
household_id; no cross-household leakage at Postgres or SDK level. - Redeem flows through EconomyService — reward redemption is a token debit; the bloc must call the economy path (via facade) alongside the catalog path; both calls are SDK-mediated, not direct.
- Claim re-uses Today's claim path —
chore_claimedfrom Catalog is the same SDK service operation as from Today; the service enforcesmaxPerDayserver-side. - Chore editor route already exists — FR-CATALOG-5 wires an entry point only; the editor page from P1/P2 Admin work is not re-built here.
isParentalguard is the existing authz check. - Live updates — a
watch*stream on catalog state keeps claim/redeem affordances current; a stale Claim button on a fully-claimed bounty is a data-integrity issue, not just UX.
Design work (ahead of build)
- Catalog browse IA — tab layout, card anatomy per kind (chore / bounty / activity / reward), hierarchy of name / token value / action CTA. No design delivered yet.
- Reward redeem flow — confirmation affordance (sheet or inline confirm), success feedback (token debit animation; ties into Money DS work), insufficient-balance error state.
- Empty states — copy per tab for "no items exist" (parental CTA) vs. "nothing eligible for you" (member). DS atom reuse; no new illustration asset for MVP-1.
- Activity lock treatment — lock icon placement, gate condition label copy ("earn X more" vs. "complete Y first"). Copy decision needed before build.
Decisions (resolved for MVP-1)
Resolved — see the MVP-1 decisions log for the canonical record, rationale, and status legend (✅ decided · ⚖️ counsel confirms · 🔜 MVP-1.x).
- D-CATALOG-1 — Eligibility display. ✅ Show ineligible items grayed-out with a short rationale ("unlocks at…" / "for ages…"), not hidden — transparency + aspiration aid motivation. Full household catalog visible.
- D-CATALOG-2 — Bounty claim mechanics. ✅ First-come-first-served (current SDK assumption) for MVP-1; admin-assigns-after-request and the claim-request flow are out of MVP-1 scope.
- D-CATALOG-3 — Post-claim state. ✅ A fully-claimed bounty shows "claimed today" (visible, disabled) rather than disappearing; remaining daily slots shown when more than one.
- D-CATALOG-4 — Redemption reversal. ✅ No undo (append-only ledger). A correction is a new compensating entry by an admin, never a mutation.
Out of scope (MVP-1)
- Template library — import common chores from a household chore store; deferred.
- Member-facing search / filter — flat scrollable list for MVP-1; search is a post-MVP addition.
- Bounty claim notifications — push alert when a bounty is claimed by another member.
- Activity gating CRUD — gate management lives in Admin; Catalog surfaces lock state read-only.
- Admin CRUD inline in catalog — all mutation paths are owned by the Admin hub.
- Photo-gated bounty claim — photo capture at claim time deferred to the chore-photo-submissions feature.