v1.14 — Coach Surfaces, Always Valuable
Coach insights are now real, personalized, and available on five core surfaces — Dashboard, Goals, Goal Detail, Achievements, Communities — with persona-aware fallback copy ensuring users never see empty space.
Summary
Before v1.14, Coach insights were client-side mocks — rule-based templates that looked like coaching but required zero backend context. Users saw the same insight copy regardless of their goals, streaks, or progress. This milestone shipped real, personalized, LLM-powered insights, unlocking Coach as a genuine product feature rather than a placeholder.
The work spans four shipping phases (65–68) and final cleanup (69). Phase 65 locked the architecture: five backend-supported surfaces, a three-rung fallback strategy (AI → persona-aware library → client template), and an intentional asymmetry between queries and mutations around the AI_DISABLED state. Phase 66 shipped the frontend composable (useAiInsights) with module-scoped caching, dismiss persistence, and the rule-based fallback template. Phase 67 wired the backend daily batch job, the persona-aware fallback library (6 personas × 4 surface types), and fixed a latent JSON-parse-failure bug that could leak garbled AI output. Phase 68 rewrote the frontend composable to use the new backend batch pack instead of client-side mutation calls, integrating insights on all five surfaces. Phase 69 (final cleanup) removed two orphaned community components, rewrote the feature documentation, added the degraded-state runbook, and landed the Coach Surface Map reference.
The result: users now see contextually accurate insights (e.g., "Time to Check In" on the dashboard if their urgent goal has been inactive >3 days) with guaranteed fallback copy when Coach is unavailable. No silent nils, no garbled output, no generic placeholder text. Every surface has clean, persona-aware guidance.
Goal
Deliver Coach Insights to five core user-facing surfaces (Dashboard, Goals, Goal Detail, Achievements, Communities). The insights are powered by LLM context inference during a daily batch job, with persona-aware fallback copy when the AI is unavailable. Insights are dismissible with 24-hour TTL and cached per-page with 15-minute in-session re-fetch. The three-rung fallback strategy (AI → persona-aware library → client template) ensures users never see empty space, silent nils, or garbled output, upholding the Coach brand as a competent, action-forward partner.
Scope — What Shipped
- Daily insight batch job (
AiCoaching::GenerateDailyInsightPackJob) — runs once per UTC day, generates personalized insights for eligible users (users with active goals), populatesRails.cacheper(user, page)key for 24 hours - Persona-aware fallback library (
Ai::Fallbacks::Insights) — 6 personas × 4 surface types (welcome_back, streak_break, check_in, generic_insight), persona-aware brand-voice copy, used when backend batch is unavailable - Frontend composable rewrite (
useAiInsights) — replaced client-request-per-page pattern with backend batch query (me.currentInsights), module-scoped Apollo query, 15-minute in-session cache per page, 24-hour dismiss TTL via localStorage,generateMockInsightas third-rung fallback (kept through v1.15 soak, deleted in v1.15) - Five integrated surfaces — Dashboard, Goals, Goal List, Achievements, Communities now render
AiInsightCardwith insights from backend batch or fallback rung - GraphQL query asymmetry —
me.currentInsightsreturns[]whenSettings.ai.batch_enabled = false(silent, allows frontend fallback rendering); mutations (getInsight,generateMilestones) raiseAI_DISABLEDerror (explicit error for user-triggered actions) - Runbook + documentation —
docs/features/ai-coach.mdrewritten with three-rung ladder, caching/session behavior, eligibility gate,currentInsightsfield semantics, and query/mutation asymmetry.docs/operations/ai-runbook.mdgains per-surface degraded-state matrix and asymmetry explanation. Coach Surface Map (docs/features/coach-surfaces.md) lands as comprehensive reference. - Orphan cleanup —
CommunityInsightsPanel.vue,CommunityComparisonModal.vueremoved (zero production callers);Goal.vue:389stale comment removed; shared examplerespects_ai_category_disabled_for_field.rbdeleted and coverage inlined intocurrent_insights_spec.rb - Bug fix —
GenerateDailyInsightPackJob#parse_insightnow threadspersonathrough to error path, routing JSON-parse failures throughfallback_insightinstead of leaking truncated raw AI output
Phases
| Phase | Name | Status | Plans | Highlights |
|---|---|---|---|---|
| 65 | Decisions + Design Contract | Shipped | 1 | Locked surfaces map, three-rung fallback, query/mutation asymmetry contract |
| 66 | Frontend Stepping Stone | Shipped | 2 | useAiInsights composable, module-scoped cache, dismiss persistence, generateMockInsight third rung |
| 67 | Backend AI Pipeline | Shipped | 3 | Daily batch job, persona-aware library, JSON-parse fix for error path |
| 68 | Composable Rewrite | Shipped | 4 | Backend batch integration, me.currentInsights query, all five surfaces live |
| 69 | Cleanup + Docs | Shipped | 5 | Orphan deletion, documentation, Coach Surface Map merge, degraded-state runbook |
Key Decisions
Three-rung fallback ladder: AI → persona-aware library → client template. Ensures users never see empty space or silent nils. The second rung is persona-aware copy from the backend, upholding the Coach brand even when the LLM is unavailable.
Query returns
[], mutations raise errors: Theme.currentInsightsquery (called on every page mount) returns an empty array when AI is disabled. Mutations (getInsight,generateMilestones) raiseAI_DISABLEDerrors. Asymmetry justified: queries fire everywhere; a hard error on every navigation degrades UX. Mutations are discrete user actions; a single error message is appropriate.Daily batch, not on-demand: Instead of calling the LLM on every user page load, insights are generated once per day for users with active goals and cached for 24 hours. Trade-off: insights are less fresh but deterministic, cacheable, and cost-controlled. Users see the same insight throughout the day (better for consistency), and the second rung fallback handles mid-day AI unavailability.
Module-scoped Apollo query: The
CURRENT_INSIGHTS_QUERYis fetched once per session per user and re-run every 15 minutes if the user navigates away and back to a page. One lazy fetch, not per-component, reduces redundant requests and keeps Apollo's normalized cache warm.Eligible-user gate on batch job: Only users with active (non-completed) goals receive the daily batch pack. Inactive users fall through to the second rung (persona-aware library) or third rung (client template). Rationale: insights are contextual to active progress; users with no active goals have no progress to surface.
Requirements Coverage
8 / 8 requirements satisfied. Full requirements list: v1.14-REQUIREMENTS.md.
| Req | Category | Outcome |
|---|---|---|
| COACH-1 | Five backend-supported surfaces | ✅ Dashboard, Goals, Goal Detail, Achievements, Communities |
| COACH-2 | Daily batch job wiring | ✅ GenerateDailyInsightPackJob shipped, runs UTC daily |
| COACH-3 | Persona-aware fallback library | ✅ 6 personas × 4 surfaces, 100% coverage |
| COACH-4 | Query/mutation asymmetry spec | ✅ currentInsights returns []; mutations raise errors |
| COACH-5 | Frontend composable rewrite | ✅ useAiInsights refactored for backend batch |
| COACH-6 | Surface integrations (all 5) | ✅ AiInsightCard rendering on all five pages |
| COACH-7 | Documentation & runbook | ✅ Feature guide, degraded-state matrix, Surface Map |
| COACH-8 | Production monitoring | ✅ Batch job execution logs, AI budget tracking via admin dashboard |
Outcomes
Users now see personalized, contextually accurate Coach insights on Dashboard, Goals, Goal Detail, Achievements, and Communities. The insights are generated daily by Coach's LLM context inference and cached for 24 hours, ensuring deterministic, cost-controlled behavior. When Coach is unavailable (AI disabled, rate-limited, or upstream error), users see persona-aware fallback copy instead of silence, maintaining the Coach brand as a competent, action-forward partner. The three-rung fallback ladder (AI → persona-aware library → client template) guarantees no user ever sees empty space, silent nils, or garbled output.
Tech Debt
- (Phase 68)
generateMockInsightis kept as the third-rung fallback through v1.15 soak window for safety. Scheduled for deletion in v1.15 along with its 8 brand-voice strings (deferred from Phase 69 per OBJ-215). - (Phase 69)
MemberProfileModal.vue,CommunityGoals.vue,CommunityMembers.vuestill carry// Mock datacomments on live surfaces. Each requires its own backend wiring task; parked for community-page v1.15+ work.
Related Artifacts
- Roadmap: v1.14-ROADMAP.md
- Milestone Audit: v1.14-MILESTONE-AUDIT.md
- Coach Surface Map: docs/features/coach-surfaces.md
- Coach Feature Guide: docs/features/ai-coach.md
- AI Operations: docs/operations/ai-runbook.md
- Git tag: v3.9.89
Related Commits
ddb93e33— feat(v1.14/phase-68): wire useAiInsights to me.currentInsights (PR #543)a4892c63— feat(ai-coaching): Phase 67 — batched insight job, persona-aware fallbacks, check-in fix (PR #541, OBJ-224)5a8781b0— feat(coach): wire GoalSageSuggests to real data; add CoachUnavailableCard (PR #540)6db54bfd— docs: resolve Coach Surfaces Map TBDs and verify surface count (PR #538, cherry-picked into v1.14)fd3cddff— docs(v1.14/phase-69.4): add v1.14 milestone CHANGELOG entries (engineering + user-facing)
Last updated: 2026-05-22