product

JQ Day Planner (Family)

Status: v1 SHIPPED 2026-07-26 (migration 585). Placement + persistence decided by the owner: dashboard, per-member, applies to the day. Inherits FAMILY_INTENT.md.

What it is

A "🗓 Plan the day" button on each member's card at /dashboard/family. JQ takes that person's context and proposes an ordered day the parent can drag to reorder, tap ✕ to drop, then keep as the day's schedule.

Privacy — de-identified by construction

The payload sent to the LLM carries no name, no birthdate, no family name — only a coarse role ("child"), an age band ("6-12"), and the day's activity lists. The member's name never leaves the backend process (gatherPlannerContext returns it separately, for the UI title only). Same posture as the ETHOS/Living-Voice walls.

Context gathered (family-day-planner.ts)

  • Age band — from birthdate → band (ageBandFromBirthdate), or "unknown".
  • Routines due todayfamily_routines, filtered by cadence for the date.
  • Family calendar commitmentsfetchFamilyEvents for the day (family-wide).
  • Growth goals — the member's enabled growth_nudges (labels only).
  • Sleep — earliest wake alarm if set, else an age-appropriate sleep window.
  • Weekend community events — opt-in fetchEvents (family-friendly), weekends only.

Data model (migration 585)

family_day_plans — one row per member per day: blocks jsonb (ordered {id,title,time_label,kind}; kind ∈ routine|meal|school|activity|event|rest|free), plus dropped jsonb — the titles the parent deselected from JQ's proposal. RLS-on / service-role-only, gated by parent_user_id.

Endpoints (routes/family.ts)

  • POST /api/family/members/:id/day-plan/generate{ blocks, member_name, is_weekend }
  • GET /api/family/members/:id/day-plan?date={ blocks }
  • PUT /api/family/members/:id/day-plan { blocks, dropped } → saves the day's plan

The dropped[] signal → goal-formation nudge (SHIPPED 2026-07-26)

Requested alongside v1, now built. FamilyGoalNudge (mounted on /dashboard/family) reads GET /api/family/goal-nudge (getRecentDeselections — last 7 days of family_day_plans.dropped, grouped by member, most-dropped first). During a quiet beat (~9s after landing, not on load) the JQ card rolls in from the bottom-right and asks "You dropped N things from {member}'s day lately — like {titles}. Want to turn one into a goal?" Picking a dropped item + a cadence seeds it as a routine (which then gets its own freshness tracking). Deterministic — no LLM, no cost. Invitation not nag: dismissible, capped once per signal (member + drop-count) per browser via localStorage, so a new pattern can re-surface but the same one won't re-nag.

Design notes for that build:

  • Signal storefamily_day_plans.dropped already accumulates per day; a nudge reads recent rows (last N days) to find a pattern ("keeps dropping homework", "always drops the evening activity").
  • Quiet-moment trigger — reuse the PresenceOrb (components/layout/) idle detection; fire at most once per session, never mid-interaction. Inherits the growth-nudge ethics wall: invitation, never a nag; dismissible; frequency-capped.
  • The form — a tiny goal-capture (title + optional cadence) that can seed a family_routines row, a growth goal, or a Downstream/Levers item. Keep it one question.
  • Additions too — when a parent adds a block not proposed, that's also signal (an unmet routine); capture symmetrically.
  • Gate/telemetry as a cost-bearing LLM surface consistent with Ask JQ (Plus).

Other follow-ons

  • Show the saved plan on the wall + collapsed card (the data's there via GET) so "apply to the day" is visible everywhere, not just in the planner.
  • Free-text situations ("a rough sleep night", "sick day") feeding the same generator.
FAMILY DAY PLANNER — Docs | HiveJournal