Family Assistant v1.5 ā Scope Document
Wadsworth Review Copy with Comments for Daedalus
Status: Draft
Date: 2026-04-21
Owner: Socrates (Backend) + Daedalus (Frontend)
Approver: Matt (pending)
Target Release: Post v1.0 stabilization
Reviewed by: Wadsworth š
Review Date: 2026-04-21 22:06 UTC
Executive Summary
Family Assistant v1.0 is feature-frozen and operational. v1.5 addresses trust and reliability gaps identified in the April 2026 audit ā human error recovery, persistent state management, and graceful degradation.
This document follows contract-first protocol: API specs are binding on backend; UI/UX requirements are binding on frontend.
Goals
| Priority | Goal | Success Metric |
|---|---|---|
| P0 | Eliminate silent data loss scenarios | User can undo any calendar mutation within 5 minutes |
| P0 | Make conflicts actionable | Conflicts don't disappear after first alert; nag until resolved |
| P1 | Ensure Family Brain availability when Gaming PC sleeps | Degrade to keyword search; no hard failures |
| P1 | Give Aundrea phone-less calendar access | Web dashboard with read-only family calendar view |
| P2 | Improve newsletter signal-to-noise | Formatted digest, not raw item dumps |
| P2 | Proactive travel notifications | "Leave in 15min" alerts based on cached travel times |
Features
1. Undo Stack (P0)
Problem: Intent engine mutations (move, cancel, rename) are destructive and irreversible. Aundrea has no "oops" path.
Solution: Persist last 5 operations with reversible snapshots.
API Contract (Socrates)
# POST /api/v1/undo
# Authorization: Bearer token (same auth as webhook)
# Request:
{
"action": "list" | "undo" | "redo",
"operation_id": "uuid" # required for undo/redo
}
# Response (list):
{
"operations": [
{
"id": "uuid",
"type": "move" | "cancel" | "rename" | "add",
"summary": "Sullivan Soccer",
"executed_at": "2026-04-21T14:30:00-05:00",
"reversible_until": "2026-04-21T14:35:00-05:00", # 5 min TTL
"can_undo": true,
"snapshot": {
"event_id": "caldav-uuid",
"previous_state": { ... }, # full iCal VEVENT snapshot
"new_state": { ... }
}
}
]
}
# Response (undo):
{
"status": "undone" | "expired" | "not_found",
"restored_event": { ... },
"message": "Sullivan Soccer restored to original time"
}
Storage: ~/.family_assistant/undo_stack.jsonl ā append-only, capped at 5 entries, 5-min TTL per entry.
Security: Operations reversible only by same Telegram user who initiated (check sender_id).
Daedalus Requirements
- Telegram Integration: Add "ā©ļø Undo" button to Hermes confirmation messages for 5-minute window
- Visual State: Show "Recently modified" list in dashboard with undo affordance
- UX Pattern: Toast notification style: "Moved Sullivan Soccer to Wednesday [Undo]"
š¬ Wadsworth Comment for Daedalus:
Consider "household-level" undo permission. Aundrea may ask Matt to undo her accidental mutation. Current spec restricts tosender_idonly ā discuss with Socrates if household admin override is needed.
2. Persistent Conflict Registry (P0)
Problem: Conflict alerts fire once and disappear. No persistent "outstanding conflicts" list.
Solution: SQLite-backed conflict registry with nag escalation.
API Contract (Socrates)
# GET /api/v1/conflicts
{
"outstanding": [
{
"id": "uuid",
"detected_at": "2026-04-21T08:00:00-05:00",
"event1": { "summary": "Sullivan Soccer", "start": "...", "end": "..." },
"event2": { "summary": "Harper Dance", "start": "...", "end": "..." },
"overlap_minutes": 30,
"status": "unacknowledged" | "acknowledged" | "resolved",
"nag_count": 2, # incremented on each heartbeat if unacknowledged
"resolution": null | { "action": "split", "choice": 1, "executed_at": "..." }
}
]
}
# POST /api/v1/conflicts/{id}/acknowledge
{ "status": "acknowledged" } # Stops nagging, keeps in registry
# POST /api/v1/conflicts/{id}/resolve
{
"resolution_action": "split" | "reassign" | "reschedule",
"choice_index": 0, # which option was selected
"executed": true | false # whether calendar was actually modified
}
Storage: SQLite at ~/.family_assistant/conflicts.db ā single table, indexed on status and detected_at.
Nag Logic:
- Heartbeat:check runs every 30m
- If unacknowledged and nag_count < 3: send alert to family group
- If nag_count >= 3: escalate to DM with "ā ļø UNRESOLVED CONFLICT" urgency
Daedalus Requirements
- Conflict Badge: Dashboard shows conflict count in header
- Conflict Detail View: Side-by-side event comparison with resolution options
- Mobile Optimization: Swipe-to-acknowledge, tap-to-resolve pattern
- Visual Priority: Red accent for unacknowledged, yellow for acknowledged, green for resolved
š¬ Wadsworth Comment for Daedalus:
"Swipe-to-acknowledge" is good mobile UX, but ensure web dashboard has equivalent click/tap affordance. Also consider: family group visibility is important for shared accountability ā DM escalation removes visibility. Discuss with Matt whether "escalate to DM" should be " louder family group message" instead.
3. Brain Offline Fallback (P1)
Problem: If Gaming PC sleeps or Tailscale is down, nomic-embed-text fails. Family Brain throws exception.
Solution: Try embeddings ā if fail, fall back to keyword search against raw email chunks.
API Contract (Socrates)
# POST /api/v1/brain/query
{
"question": "What do the kids need for the field trip?",
"force_keyword": false # optional: bypass embeddings entirely
}
# Response:
{
"answer": "...",
"mode": "semantic" | "keyword_fallback", # transparency to user
"sources": [...],
"confidence": "high" | "medium" | "low",
"embeddings_available": true | false
}
Fallback Logic:
1. Try nomic-embed-text on Gaming PC (timeout: 5s)
2. If timeout/error: search ChromaDB metadata (subject, sender, date) with keyword matching
3. If still no results: search raw email body text (cached in ~/.family_assistant/email_cache/)
Health Check: Add embeddings_reachable to /health endpoint.
Daedalus Requirements
- Mode Indicator: Show subtle "offline mode" badge when using keyword fallback
- Confidence Display: Lower confidence ā suggest rephrasing question
- No User Action Required: Should be transparent, but honesty about quality is important
š¬ Wadsworth Comment for Daedalus:
"Subtle offline mode badge" is good ā avoid alarmist UI. Consider: "keyword mode" badge with tooltip "Semantic search unavailable. Results may be less precise." This sets correct expectations without causing concern.
4. Family Dashboard ā Read-Only Calendar View (P1)
Problem: Aundrea doesn't want to install CalDAV clients. Needs phone-less access to family calendar.
Solution: Web dashboard served from hoffdesk-api, read-only CalDAV view.
API Contract (Socrates)
# GET /api/v1/calendar/events
# Query params:
# start=2026-04-01T00:00:00-05:00
# end=2026-04-30T23:59:59-05:00
# include_travel=true # enrich with travel time from location cache
{
"events": [
{
"id": "caldav-uuid",
"summary": "Sullivan Soccer",
"start": "2026-04-21T15:00:00-05:00",
"end": "2026-04-21T16:00:00-05:00",
"location": "Golrusk Pet Center",
"travel_time_minutes": 12,
"travel_from_home": true,
"status": "confirmed",
"who": ["Sullivan"],
"color": "#3b82f6" # auto-assigned per-person
}
],
"conflicts": [ // include outstanding conflicts in same response
{ "id": "...", "event1_id": "...", "event2_id": "...", "overlap_minutes": 30 }
]
}
# GET /api/v1/calendar/today
# Shortcut for today's events
# GET /api/v1/calendar/week
# Query param: week_offset=0 (current), -1, +1, etc.
Caching: 5-minute cache on Radicale reads to reduce load.
Auth: Same TELEGRAM_BOT_TOKEN validation as webhook (token in header, IP allowlist optional).
Daedalus Requirements
- Calendar Views: Day, week, month (month optional for MVP)
- Week View Default: Family planning happens at weekly granularity
- Person Colors: Sullivan = blue, Harper = pink, Family = green, etc. (see design-tokens/)
- Conflict Highlighting: Red stripe on conflicting events
- Travel Time: Small text under location: "12 min from home"
- Mobile-First: Touch-friendly, swipe between days/weeks
- No Login Required: Shareable URL with token (time-limited, revokeable)
š“ Wadsworth Concern for Daedalus:
This is the largest scope item in v1.5. Week/month calendar views + touch gestures + person colors + conflict overlay + travel markers is substantial frontend work.Recommended refinement:
- MVP P1: Day view only, simple list/timeline, no swipe gestures
- P1.5: Week view (grid), swipe gestures
- P2: Month viewSizing request: Please estimate effort before final P1 commit. If this exceeds ~3-4 days of focused work, recommend MVP scope reduction.
5. Newsletter Digest v2 (P2)
Problem: Info items dump raw text to Telegram. Low signal-to-noise.
Solution: LLM-summarized digest with categories.
API Contract (Socrates)
# POST /api/v1/digest/generate (internal, called by pipeline)
{
"items": [...], // raw items from newsletter parser
"style": "brief" | "detailed" // brief for daily, detailed for weekly
}
# Response:
{
"sections": [
{
"category": "School",
"icon": "š",
"items": [
{
"summary": "Field trip permission due Friday",
"who": ["Sullivan"],
"action_required": true,
"deadline": "2026-04-25"
}
]
},
{
"category": "Sports",
"icon": "ā½",
"items": [...]
}
],
"markdown": "š **School**\n⢠Field trip...\n\nā½ **Sports**..." // formatted for Telegram
}
Categories: School, Sports, Medical, Community, Other (configurable in family.yaml)
Prompt: Single LLM call with category definitions, returns structured JSON.
Daedalus Requirements
- Digest View: Dashboard card with collapsible sections
- Category Icons: Consistent emoji/icon mapping (design-tokens/)
- Action Badges: "Needs action" indicator on items with deadlines
- Mark Done: Inline button to dismiss (triggers intent engine)
š¬ Wadsworth Comment for Daedalus:
Good candidate for HTMX progressive enhancement: collapsible sections that work without JS, enhanced with smooth animation when JS available. "Mark Done" button should have immediate visual feedback (strike-through or fade) before backend confirmation.
6. Proactive Travel Notifications (P2)
Problem: Location cache has travel times but doesn't alert when to leave.
Solution: Heartbeat-driven "leave by" notifications.
API Contract (Socrates)
# GET /api/v1/notifications/leave-by
// Called by heartbeat 30 min before event start
{
"upcoming": [
{
"event_id": "...",
"summary": "Sullivan Soccer",
"start_time": "2026-04-21T15:00:00-05:00",
"leave_by": "2026-04-21T14:43:00-05:00", // start - travel_time - 5min buffer
"travel_time_minutes": 12,
"location": "Golrusk Pet Center",
"notified": false
}
]
}
# POST /api/v1/notifications/mark-sent
// Mark as notified to prevent duplicate alerts
Trigger Logic:
- Heartbeat:check scans next 2 hours of events
- If event.start - travel_time - 5min_buffer <= now + 30min and not notified: send alert
- Only notify once per event
Hermes Message:
š Leave soon for Sullivan Soccer
Leave by: 2:43 PM (12 min drive)
Location: Golrusk Pet Center
Daedalus Requirements
- Notification Preferences: Per-user toggle (Aundrea wants these, Matt might not)
- Visual Timeline: Dashboard shows "leave by" time as separate marker before event
- Snooze: "Remind me in 5 minutes" button
š¬ Wadsworth Comment for Daedalus:
Snooze UX pattern: after snooze, show "Reminding at 2:38 PM" with cancel option. Consider: travel notifications may need tighter cadence than 30min heartbeat ā discuss with Socrates if dedicated travel-alert cron (5-min) is feasible.
Daedalus Deliverables Summary
UI Components Required
- Undo Toast ā Floating notification with countdown timer
- Conflict Badge ā Header indicator with count
- Conflict Detail Panel ā Side-by-side event comparison
- Calendar Week View ā Grid layout with person colors
- Calendar Day View ā Timeline with travel markers
- Digest Card ā Collapsible category sections
- Offline Badge ā Subtle mode indicator
Design Token Requirements
| Token | Value | Usage |
|---|---|---|
--color-person-sullivan |
#3b82f6 (blue-500) |
Sullivan events |
--color-person-harper |
#ec4899 (pink-500) |
Harper events |
--color-person-family |
#22c55e (green-500) |
Family events |
--color-conflict-unack |
#ef4444 (red-500) |
Unacknowledged conflicts |
--color-conflict-ack |
#eab308 (yellow-500) |
Acknowledged conflicts |
--color-travel-marker |
#6b7280 (gray-500) |
Leave-by indicators |
--undo-toast-ttl |
300s |
Undo button visibility duration |
Shared Assets
Place in shared/design-tokens/family-assistant/:
- colors.json ā Person assignments, conflict states
- icons.json ā Category icons for digest
- spacing.json ā Calendar grid dimensions
š“ Wadsworth Action Required:
Daedalus to provide effort estimate for dashboard (Feature 4) before final scope approval. Recommend day-view MVP if estimate exceeds 3-4 days.
Wadsworth Review Summary
Strengths of v1.5 Scope
- Addresses real pain points (undo, persistent conflicts)
- Contract-first approach with clear API specs
- Well-prioritized (P0/P1/P2)
- Graceful degradation mindset (brain fallback)
Concerns
| Feature | Concern | Severity |
|---|---|---|
| Undo | Household-level permissions | Low |
| Conflicts | DM escalation reduces visibility | Low |
| Dashboard | Scope risk ā week/month views are large | Medium |
| Travel | 30min heartbeat may be too coarse | Low |
Recommendations
- ā Approve scope with dashboard MVP caveat (day-view only for P1)
- š Request Daedalus sizing estimate before final commit
- š Discuss undo permissions and conflict escalation with Matt
Routing
- Socrates: Backend implementation ready to proceed
- Daedalus: Frontend scope needs sizing confirmation
- Wadsworth: Coordination, approval tracking, scope refinement
Reviewed by: Wadsworth š
Review Date: 2026-04-21 22:06 UTC
Original document: shared/project-docs/family-assistant/v1.5/SCOPE.md
This review copy: shared/project-docs/family-assistant/v1.5/SCOPE-wadsworth-review.md