Skip to main content

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.

TypeFunctional
Layer (build approach)Frontend — PDF gen (built)
RICER 4 × I 0.5 × C 100% / E 0.5 = 4.0 · Tier MVP-1 (built)
KPI (summary)Printable generation count
Traces tofeature print-printables · C4 components
Depends onCatalog · 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_generated event 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 + printing dialog 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.

EventWhenKey properties (coarse)Feeds
printable_generatedprintService.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 = false When Today's _HomeHeader builds Then PrintMenu is 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 > 0 When the PDF row renders Then a right-aligned hint shows the token amount (same visual weight as the existing bounty hint).
  • Given tokenValue = 0 When 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.printPdf call When it returns Then printable_generated {type, choreCount} is emitted via the consent-gated analytics substrate.
  • type = per_kid or per_room; choreCount is 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 DartPrintableChoreList has zero dart:ui imports; headless unit tests cover buildMemberList / buildPlaceList without a Flutter engine. The printing dep lives in the app layer only.
  • One data pathPrintMenu reads chores from TodayChoresBloc.state (already in memory); rooms from PlacesRepository. No direct drift/supabase access in the print widget.
  • Feature flag gate (FR-PRINT-1) lives in _HomeHeader's BlocBuilder — read HouseholdBloc.state.household?.featureFlags.printableLists; no new bloc or repository needed.
  • Offline — all PDF inputs live in the TodayChoresBloc state (Drift write-through cache); no network call at print time satisfies the offline-first contract.
  • membersForRoom is not used here — room filter uses Chore.placeId; the picker resolves rooms via PlacesRepository.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 _choreRow to 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 estimateMin is 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.