Family Wall — JQ voice actions ("do it", not just "ask")
Status: ✅ BUILT (2026-07-18, migration 539) — see INDEX.md → "JQ voice actions +
device identity + reminders". This doc is the original design; the
shipped shape matches it (device identity location_kind/owner_member_id, the
attribution rule, family_reminders, the pure action detector + LLM fallback, the
ask-who flow). Built on the on-wall-editing PR (#1393, migration 538).
The ask
Today "Ask JQ" on the wall is read-only — it answers questions grounded in the
wall's current content (family-wall-ask.ts +
the deterministic pre-filter family-wall-prefilter.ts).
Make it act: "add milk to the shopping list", "we're having tacos tonight",
"leave a note that grandma visits at 5", "remind Ada to pack her cleats".
Foundation: wall device identity (shared vs. personal)
Both JQ actions and the intercom need to know whose
wall this is. New per-wall settings on family_wall_displays:
location_kind text—'shared'(kitchen/living-room, default) |'personal'(a bedroom wall belonging to one member).owner_member_id uuid— for'personal'walls, thefamily_members.idthis wall belongs to. NULL on shared walls.
Owner sets both in wall settings. (location_kind is also the intercom's shared-vs-
bedroom signal + quiet-hours anchor — one column, two features.)
Attribution rule (owner-specified)
When an action needs a who (a reminder/task assigned to a person):
- Personal wall (
location_kind='personal') → auto-attribute toowner_member_id. No prompt. "Remind me to…" just works for that member. - Shared wall → JQ asks "who's this for?", lists the family members (voice or tap), and attributes to the chosen member.
Actions with no "who" (shopping item, tonight's dinner, a general note) never prompt — they're household-shared already.
Action taxonomy (v1)
| Utterance intent | Executes | Needs "who"? |
|---|---|---|
add <item> to shopping | shoppingAddFromWall (#1393) | no |
set/what's for dinner <meal> | mealSetFromWall (#1393) | no |
leave/post a note <text> | noteAddFromWall (#1393) | no |
remind/assign <who?> <task> | family_reminders (new) | yes |
Reminder/task model (new — the one net-new table)
A one-off, member-attributed reminder — deliberately NOT a recurring family_routine
(those stay the chore-chart system; the user already deferred wall-created routines).
family_reminders:
id,parent_user_id(family owner),family_member_id(the assignee — the "who"),text(≤200),due_at timestamptz NULL(optional; JQ parses "tomorrow", "at 5"),done boolean default false,created_by,created_at.
Shown on the wall on the assignee's member card (a small "📌 reminders" list), tappable
to complete on an allow_checkin wall. Surfaced in resolveWallView like pets/points.
Flow
- JQ hears/reads the utterance (existing mic + text paths).
- Classify intent locally first — extend
classifyIntentin the pre-filter to detect the 4 action intents + extract the payload (item/meal/note/reminder text). Deterministic where possible (private, no LLM); fall to the LLM only to parse messy phrasing into{action, payload, who?}. - Gate: actions require
allow_askANDallow_editing. A wall with Ask-JQ on but editing off → JQ answers "I can't change the wall — ask a grown-up to turn on Add-from-wall." (read-only stays the default). - If the action needs a who and the wall is shared → JQ asks, collects the member.
- Execute via the (existing or new-reminder) endpoint, then JQ confirms out loud what it did ("Added milk to the shopping list."). Confirm-after, not confirm-before (low stakes; the wall refetches so the change is visible immediately).
Endpoint
Extend the existing public POST /api/family/wall/view/:token/ask to return a
structured { action, needs_who, members? } when it detects an action, and add
POST /api/family/wall/view/:token/act (token-scoped, allow_ask+allow_editing,
re-scoped to the wall's parent) that performs { action, payload, member_id? }.
Both go in the route-auth INTENTIONAL_PUBLIC allowlist. Rate-limit like ask.
Safety
- Writes gated behind the explicit
allow_editingopt-in (a leaked token still can't act unless the owner turned editing on) — same bright line as #1393. - Everything re-scoped to the wall's own parent; the "who" can only be a member of that family. A shared wall can't assign a reminder to another household.
- Bedroom/minor personal walls inherit the [[project_family_frame]] counsel posture.
Sequencing
- #1393 merges (needs migration 538 applied to prod).
- This PR: migration (
location_kind,owner_member_id,family_reminders) → settings UI (device identity + assignee) → reminder service + wall projection → JQ action classify/execute + confirm.