Skip to content

ai-partner: daily proactive prompt generation job #1465

@CraigBuckmaster

Description

@CraigBuckmaster

Parent epic: #1446 (Amicus — AI Study Partner v1)
Phase: 4 · Size: M · Depends on: #1450 (proxy), #1452 (profile)

Generate one proactive "Amicus noticed..." prompt per user per day. Runs on app open (not background — no battery tax); cached 24h keyed on profile hash + date.


Architecture decision: where does this run?

Client-triggered, server-generated, client-cached.

  • App on open checks local cache; if fresh (<24h and matching profile hash), use cached prompt
  • If stale or missing, hit a new proxy endpoint /ai/daily-prompt which generates via Haiku
  • Cache result in user.db

Rationale:

  • No scheduled jobs (mobile → no reliable background execution)
  • Zero-cost when user doesn't open the app
  • Profile data stays on device (only compressed summary sent to proxy)

Files to create

  • ai-proxy/src/dailyPrompt.ts — new endpoint in the Cloudflare Worker
  • app/src/services/amicus/dailyPrompt.ts — client service that checks cache, calls proxy if needed
  • app/src/hooks/useDailyPrompt.ts — hook for HomeScreen to consume
  • app/src/services/amicus/__tests__/dailyPrompt.test.ts

Files to modify

  • ai-proxy/src/index.ts — route POST /ai/daily-promptdailyPrompt.handler
  • app/src/db/userDatabase.ts — migration adds amicus_daily_prompt_cache table
  • app/src/db/userQueries.ts / userMutations.ts — add read/write for the cache

Server-side endpoint (/ai/daily-prompt)

Request:

{
  profile_summary: string;
  last_5_chapters: string[];     // e.g., ["jeremiah:29", "jeremiah:30", ...]
  client_date: string;            // "YYYY-MM-DD" in user's timezone
}

Response:

{
  prompt_text: string;          // e.g., "Amicus noticed you've been deep in Jeremiah..."
  seed_query: string;            // what happens when user taps the card
  cached_until: string;          // ISO timestamp
}

System prompt for Haiku:

Generate a brief "Amicus noticed..." proactive study prompt for this user based on their recent reading pattern. It should reference something specific they've been studying and suggest a direction they haven't explored yet. Length: 2 sentences. Warm tone, never preachy. End with a question they can tap to explore.

User profile: {profile_summary}
Recent chapters: {last_5_chapters}

Cost: ~$0.002 per generation (Haiku + prompt caching on the system prompt). One call per user per day.

Cache schema (migration in userDatabase.ts)

CREATE TABLE IF NOT EXISTS amicus_daily_prompt_cache (
  id INTEGER PRIMARY KEY CHECK (id = 1),   -- singleton
  date TEXT NOT NULL,                      -- 'YYYY-MM-DD' (user local)
  profile_hash TEXT NOT NULL,
  prompt_text TEXT NOT NULL,
  seed_query TEXT NOT NULL,
  generated_at TEXT NOT NULL
);

Client service

export async function getDailyPrompt(): Promise<DailyPrompt | null>;

Flow:

  1. Compute current profile hash (from ai-partner: compressed profile generator #1452's profile cache)
  2. Read cache row; if date == today AND profile_hash == current_hash, return cached
  3. Otherwise call proxy /ai/daily-prompt with profile + recent chapters
  4. Persist to cache table
  5. Return prompt

Graceful degradation:

  • If proxy call fails (offline, 5xx): use most recent cache even if stale; show null if no cache at all (no UI for ai-partner: home screen card component #1466 to render)
  • NEVER block the app or show error banner for daily-prompt failures — this is optional content

Profile hash

Deterministic hash of the compressed profile prose. Cache invalidates when user's study behavior shifts enough to change the profile prose.

Performance

  • Cache hit path: <10ms (single DB read)
  • Cache miss path: <3s (proxy + generation + write)
  • Runs only on app open — zero background cost

Acceptance criteria

  • Worker endpoint /ai/daily-prompt generates valid Haiku response
  • Response always includes prompt_text, seed_query, cached_until
  • Client cache hit returns same prompt within 24h
  • Cache invalidates when profile_hash changes
  • Offline / 5xx gracefully falls back to stale cache or returns null
  • Proxy enforces auth + rate limit on this endpoint too (not exempt)
  • Cost measured: < $0.003 per user per day
  • Unit tests cover: cache hit, cache miss, profile change invalidation, offline fallback
  • No any types; lint clean

Out of scope

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions