Today screen — Jobs-to-be-Done, chore/bounty grouping, analytic funnels (2026-06-19)
Binding definition for building the Today screen out. Grounds the chore vs bounty grouping in the SDK model and ties every section to a measurable funnel so analytics map to real jobs, not vanity counts.
The model (load-bearing distinction)
From client_sdk Chore:
Expectation (ChoreKind.expectation) | Bounty (ChoreKind.bounty) | |
|---|---|---|
| Pays tokens | NEVER (tokenValue == 0, invariant 1) | Yes (tokenValue > 0) |
| Assignment | None — applies to every eligible kid (by minAge) | Unassigned; a kid claims it (claimBounty → claimedByMemberId) |
| Meaning | "the cost of being part of the family" — the scaffold | opt-in, freely chosen — the economy |
| Interaction | mark done → submit → approve (0 tokens) | claim → do → submit → approve → tokens |
Completion always flows through submitCompletion → a pending Approval
(ApprovalPolicy.manual) or an immediate auto-resolution
(ApprovalPolicy.auto, still through ApprovalService). Tokens move only on
approval (invariant 4).
The current Today screen renders every chore × every kid with a toggle — correct-ish for expectations, wrong for bounties (a bounty belongs to its one claimer, not all kids). This doc fixes that.
Jobs to be done
- Parent: "When I open Today, show me what needs my decision (approvals), let me trust the day is on track, and let me nudge without nagging."
- Kid: "Show me what I have to do (my expectations), how I can earn extra (bounties to claim), and let me mark things done myself."
Sections (each maps to one funnel)
- Expectations — the recurring "should-dos", grouped by kid (every
eligible kid), no tokens. Mark-done → submit. (Filter:
kind == expectation, active, expected in the current period.) - Bounties — split: a claimable pool (unclaimed bounties any eligible
kid can grab → Claim) and claimed bounties shown under their claimer
to finish (submit). (Filter:
kind == bounty, active.) Distinct affordance: claim, not a per-kid toggle. - Approvals (parent members only) — the pending submissions queue
(
watchPendingApprovals) with approve / reject. The parent's oversight surface; hidden for child members.
Deferred (not this pass): Goals, spend-requests, rooms toggle, print.
Analytic funnels (instrument as we build — via MixpanelEffect.trackEvent)
Event names + properties below. The MixpanelEffect sanitizer hashes id-keys
and drops financial/oversized keys, so ids are safe to pass.
Funnel 1 — Expectation completion (routine adherence; the scaffold working)
today_section_shown {section:'expectations', count}
→ chore_completion_submitted {chore_id, kind:'expectation'}
→ chore_completion_approved {chore_id, kind:'expectation', resolved_by}
- Watch: submitted ÷ shown (are kids doing their part), approved ÷ submitted (are parents keeping up on the no-token loop).
Funnel 2 — Bounty earning (the core motivation/economy loop)
today_section_shown {section:'bounties', count, claimable_count}
→ bounty_claimed {chore_id, token_value_bucket}
→ chore_completion_submitted {chore_id, kind:'bounty'}
→ chore_completion_approved {chore_id, kind:'bounty', resolved_by}
- Watch: claimed ÷ shown is the headline (does the economy pull?), and
submitted ÷ claimed (do claims convert to work).
token_value_bucketis a coarse band (e.g.1-3 / 4-9 / 10+), never the raw amount (privacy).
Funnel 3 — Parent approval (parent engagement; stalls predict churn)
today_section_shown {section:'approvals', count}
→ approval_resolved {approval_id, decision:'approved'|'rejected'}
- Watch: resolved ÷ shown + time-to-resolve (a growing pending pile = a parent falling off → churn signal).
Page-level [page]: HomeRoute is already emitted by the nav observer; these
section/action events sit under it.
Where the events fire (so funnels are honest)
today_section_shown— once per section per Today load, when the section has ≥1 item (from the Today bloc on load).bounty_claimed/chore_completion_submitted— in the Today bloc, on the successful repository call.chore_completion_approved/approval_resolved— in the bloc, on the successful approve/reject (carryresolved_by= manual | auto | parent).
Build notes
ChoresRepositorygainsclaimBounty; a newApprovalsRepository(watchPendingApprovals,approve,reject).- The Today bloc loads the three sections, exposes parent-vs-kid role (approvals
visible to parents only), and takes the
MixpanelEffectProviderto fire the funnel events (Effect Provider → Bloc is allowed, separation_of_concerns). - Domain molecules (per-screen): keep
TodayChoreRowfor expectations; add aBountyRow(claim / claimed-with-submit) and anApprovalCard(approve / reject). Promote to the DS only if a second screen proves them. - Honest states: a submitted expectation/bounty shows "submitted, waiting for approval" (partial), not a tick (mirrors the current TodayChoreRow).