šŸ“„ SCOPE-wadsworth-review.md 15,202 bytes Apr 21, 2026 šŸ“‹ Raw

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 to sender_id only — 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 view

Sizing 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

  1. Undo Toast — Floating notification with countdown timer
  2. Conflict Badge — Header indicator with count
  3. Conflict Detail Panel — Side-by-side event comparison
  4. Calendar Week View — Grid layout with person colors
  5. Calendar Day View — Timeline with travel markers
  6. Digest Card — Collapsible category sections
  7. 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

  1. āœ… Approve scope with dashboard MVP caveat (day-view only for P1)
  2. šŸ“‹ Request Daedalus sizing estimate before final commit
  3. šŸ“‹ 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