Print & printables — requirements
Epic. MVP-1 requirements breakdown for the printable chore lists (see the feature architecture). The PDF generation pipeline, per-kid list, and per-room list are already built (
PrintableChoreList+PrintMenu). MVP-1 remaining = envelope-column polish on_choreRow, feature-flag gate on the header button, and analytics rename.
| Type | Functional |
| Layer (build approach) | Frontend — PDF gen (built) |
| RICE | R 4 × I 0.5 × C 100% / E 0.5 = 4.0 · Tier MVP-1 (built) |
| KPI (summary) | Printable generation count |
| Traces to | feature print-printables · C4 components |
| Depends on | Catalog · Today |
Success criteria (definitive KPI)
Success = printable_generated count ≥ 2 per active household per month
(offline-friendly artifact); zero PDF-generation errors at P99.
- Metric:
printable_generatedevent count, grouped by household (hashed), per 30-day window. - Target: ≥ 2 events/active household/month, sustained after launch.
- Why this bar: printables are a low-frequency utility — a household that refreshes their chore chart once a fortnight is extracting full value. A higher bar would misread occasional-but-intentional use as failure.
- Guardrail (errors): zero unhandled PDF-generation exceptions at P99;
the headless pure-Dart builder +
printingdialog must not throw on any supported target. - Baseline: low-traffic utility feature — no prior data. Treat ≥ 2/month as the launch hypothesis; re-baseline from the first cohort's wk-2 data.
Analytics — events to record
Internal-BI bucket only — content-free, no child identity, never marketing (see Security). A child's display name may appear on the printed artifact — that is an operational household artifact, not an analytics payload.
| Event | When | Key properties (coarse) | Feeds |
|---|---|---|---|
printable_generated | printService.printPdf called (success path) | type (per_kid|per_room), choreCount (bucket: 0 / 1–5 / 6–15 / 16+) | primary KPI → generation count/household/month |
Current code fires
chore_list_printed {scope, count}— FR-PRINT-5 renames and aligns properties. No member id, child name, place name, or chore title ever enters the event payload.
Scope
The printable surface produces A4 PDF chore charts from data already resident
on-device — no network call is required at print time. Two layouts: per-kid
(eligible expectations + claimed bounties for one member) and per-room
(chores tagged to a Place). The household opts in via printableLists (default
ON). MVP-1 ships the built foundation; remaining work is the three polish items
below.
Functional requirements
FR-PRINT-1 — Feature flag gate on the print button
Priority: P1 · Status: 🔨 partial — FeatureFlagId.printableLists exists
and defaults ON; PrintMenu is not yet hidden when the flag is off.
Statement. As a household that has opted out of printable lists, I never see
the print icon in the Today header.
Acceptance
- Given
featureFlags.printableLists = falseWhen Today's_HomeHeaderbuilds ThenPrintMenuis absent. - Given
featureFlags.printableLists = true(default) When Today builds Then the print icon is present. - The flag is read from
HouseholdBloc.state.household?.featureFlags; no SDK call at render.
FR-PRINT-2 — Per-kid printable list
Priority: P1 · Status: ✅ built (PrintableChoreList.buildMemberList)
Statement. As a parent, I can print one member's chore list — their eligible
expectations plus claimed bounties — as a PDF.
Acceptance
- Given a member with eligible chores When I tap their row in the print sheet Then a PDF is produced containing exactly
choresForMember(member, allChores)rows. - Given a member with no eligible chores When the PDF builds Then the calm empty-state line renders (no blank page, no throw).
- Age eligibility (
_eligibleByAge) and bounty-claim filter match the Today surface rules.
FR-PRINT-3 — Per-room printable list
Priority: P1 · Status: ✅ built (PrintableChoreList.buildPlaceList)
Statement. As a parent, I can print a room's chore list — all chores tagged
to a Place — as a PDF to post in that room.
Acceptance
- Given a place with tagged chores When I select it from the room picker Then the PDF contains exactly
choresForPlace(place, allChores)rows. - Given no places configured When the room picker opens Then the empty-state message renders (no crash).
FR-PRINT-4 — Envelope-column polish on the chore row
Priority: P1 · Status: ❌ not built — _choreRow renders a token hint for
bounties only (tokenValue > 0); expectations show nothing; neither reflects the
envelope model.
Statement. As a parent reading the printed chart, every earning chore shows a
consistent token-amount hint on the right, so the chart is self-explanatory
alongside the app's envelope token economy.
Acceptance
- Given any chore (expectation or bounty) with
tokenValue > 0When the PDF row renders Then a right-aligned hint shows the token amount (same visual weight as the existing bounty hint). - Given
tokenValue = 0When the row renders Then no hint column appears (no empty stub or trailing gap). - The hint copy comes from an
Strings.print*i18n key — no hard-coded text in the builder. - The exact label copy (e.g. "earns X tokens" vs a numeric badge) is resolved via the Open decisions below before build.
FR-PRINT-5 — Analytics event rename and property alignment
Priority: P2 · Status: 🔨 partial — chore_list_printed fires today; rename
and coarse-bucket property alignment remain.
Statement. The printable_generated event fires on every successful print
trigger with coarse, content-free properties, so the KPI is measurable from day one.
Acceptance
- Given a successful
printService.printPdfcall When it returns Thenprintable_generated {type, choreCount}is emitted via the consent-gated analytics substrate. type=per_kidorper_room;choreCountis a coarse bucket (0 / 1–5 / 6–15 / 16+).- No child name, member id, place name, or chore title in the payload.
Architecture considerations
- PDF generation stays pure Dart —
PrintableChoreListhas zerodart:uiimports; headless unit tests coverbuildMemberList/buildPlaceListwithout a Flutter engine. Theprintingdep lives in the app layer only. - One data path —
PrintMenureads chores fromTodayChoresBloc.state(already in memory); rooms fromPlacesRepository. No directdrift/supabaseaccess in the print widget. - Feature flag gate (FR-PRINT-1) lives in
_HomeHeader'sBlocBuilder— readHouseholdBloc.state.household?.featureFlags.printableLists; no new bloc or repository needed. - Offline — all PDF inputs live in the
TodayChoresBlocstate (Drift write-through cache); no network call at print time satisfies the offline-first contract. membersForRoomis not used here — room filter usesChore.placeId; the picker resolves rooms viaPlacesRepository.getPlaces()only.
Design work (ahead of build)
Mostly already designed and built — A4 layout, bottom sheet, per-kid rows, and room picker are all shipped. Remaining:
- Envelope-column label copy (FR-PRINT-4) — agree on the right-side hint
text ("earns X tokens" / numeric / something else) consistent with the envelope
model naming; update
Strings.print*keys before the row polish ships. Note: PDF fonts have no emoji glyphs — the label must be text-only.
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-PRINT-1 — Label copy (FR-PRINT-4). ✅ "earns X tokens" — aligns with the envelope/economy terminology; patch
_choreRowto match. - D-PRINT-2 — Weekly multi-day layout. 🔜 MVP-1.x. MVP-1 prints single-day / per-member + per-room lists only.
- D-PRINT-3 — Timed-chore indicator. ✅ Print a small duration hint ("~15 min") when
estimateMinis set — supports the visual-timer/estimate model.
Out of scope (MVP-1)
- Weekly / multi-day routine grid printable → post-MVP-1.
- Per-member envelope balance summary on the artifact → post-MVP-1.
- Share-sheet export (PDF share vs print dialog) → post-MVP-1.
- Visual timer indicator on the printed row → deferred pending open decision.