feat(phase-d): conversational coaching layer — D1–D15 - #5
Open
simlirette wants to merge 17 commits into
Open
Conversation
- AthleteCoachingState: generation_mode + active_plan (NotRequired fields, D1)
- AthleteModel: journey_phase, recovery/onboarding overlays, 3 thread_id columns
- Alembic 0011: 6 new columns on athletes table (journey_phase, overlays, thread IDs)
- CoordinatorService: dispatch() routing matrix (A2 §Matrice de routage), thread lifecycle
(create/resume per PERSISTENT_THREAD_FIELDS), advance_journey_phase() with transition
validation (A2 §Transitions valides)
- POST /coordinator/dispatch + GET /coordinator/state/{athlete_id} routes
- main.py: coordinator_router registered
- TDD: test_coordinator_service.py (routing matrix, thread lifecycle, transitions)
test_coordinator.py (endpoint smoke tests)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…-006, DEP-C9-008, DEP-C10-003) - ClinicalContextFlag: Literal tca/red_s/ots_nfor type alias - IntentClassification + IntentClassificationRequest (DEP-C10-003): intent routing contracts with specialist_chain max-3 validator, confidence bounds, language detection, clinical acknowledgement flag - Recommendation + RecommendationMode (DEP-C4-006): B3 validators REC1 (INTERPRETATION mode field constraints) and REC2 (trigger↔mode mapping); adds INTERPRETATION mode for conditional chat consultations - EnergyCoachView (DEP-C9-008 schema): aggregated energy + load view for Energy Coach — discipline loads, allostatic score, intensity cap, recent check-ins, pattern flags - CrossDisciplineLoadV1 + V2 + DisciplineLoadDetail (DEP-C4-004): V1 session counts (backwards-compatible), V2 adds z-score, leg-impact index, session-day flags - 49 new tests (all green), 0 mypy errors, 0 ruff violations Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- classify_intent_service: gateway LLM (Haiku 4.5) with XML <contract_payload> parser,
800ms timeout, 1 retry on APITimeoutError, track_agent_call("classify_intent")
- classify_intent_builder: build_classify_intent_request from AthleteModel — extracts
athlete_id, journey_phase, sports (from JSON), clinical_flag, conversation context;
truncates last_3_intents to 3
- System prompt loaded from docs/prompts/classify-intent.md with inline fallback
- 20 new tests (all green), 0 mypy errors, 0 ruff violations
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…+ handle_session_log
- chat_turn.py: ephemeral conversational graph; run_chat_turn() orchestrates
classify_intent → HeadCoach direct (1 LLM call) or specialist+synthesis (2 calls)
- handle_session_log (DEP-C4-001): Lifting consulted when RPE delta ≥ 1.5 or
recent_elevated_rpe_count ≥ 2; else HEAD_COACH_DIRECT
- head_coach_view_builder: build_head_coach_view() from AthleteModel
- HeadCoachView schema (chat context snapshot, expandable in D6)
- ChatMessageModel + Alembic 0012: chat_messages table for history persistence
- POST /chat/message + GET /chat/history/{athlete_id} routes (ownership-gated)
- 13 new tests (all green), 0 mypy errors, 0 ruff violations
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ssDisciplineInterferenceService - chat_turn: chain ≤3 specialists with prior_chain_specialist_notes (§10.1.2), CLINICAL_ESCALATION_IMMEDIATE (0 LLM, hardcoded resources), OUT_OF_SCOPE (HC bounded), CLARIFICATION_NEEDED (HC intro + tappable axes in result), clinical_context_active_acknowledged → flag injected in specialist payload (§10.1.4) - CrossDisciplineInterferenceService (DEP-C4-004): compute_cross_discipline_load_v1 counts sessions by discipline from active_plan dict; cycling/biking aliased - 24 tests (all green), 0 mypy errors, 0 ruff violations Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…-001, DEP-C9-008) - RecoveryCoachView schema + build_recovery_view() (mean_vs_prescribed_delta_7d, rpe_avg) - EnergyCoachView builder build_energy_view() from EnergySnapshotModel (D2 schema) - run_injury_report() in chat_turn: Recovery consulted, escalate_to_takeover detection, ValueError on missing injury_payload_draft (RA7) - 9 tests: 5 view builder, 4 injury report Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- onboarding.py: run_onboarding_start() + run_onboarding_respond() with
in-memory thread state, block progression, resume-after-abandon support
- Thread ID format: {athlete_id}:onboarding:{uuid4}, stored on AthleteModel
- Blocs 1-3: accueil, profil de base, objectif/horizon — HITL interrupt after each
- POST /onboarding/start + POST /onboarding/respond routes
- 12 tests: 8 graph (start, respond, resume), 4 API smoke tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…-C3-003) - Blocs 4-6: sport_history, methodology_preferences (DEP-C4-002), scope_confirmation - Bloc 6 completion triggers journey_phase=baseline_pending_confirmation (handoff) - suspend_onboarding_for_injury(): stores suspended_onboarding_block on athlete - resume_onboarding_after_recovery(): resumes at suspended block post-takeover; returns None if no suspended block (non-onboarding injury path) - Updated D7 test: bloc 3 response leaves 3 more blocks (in_progress, not completed) - 7 D8 tests Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- run_followup_start(): creates thread, returns present_baseline step - run_followup_respond(): 2-step HITL flow — feedback → confirm_first_plan → steady_state - Objective adjustment path: onboarding_reentry_active=True flag returned - POST /followup/start + POST /followup/respond routes - 6 tests Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- run_recovery_takeover_start(): activates overlay, stores previous_journey_phase, suspends active plan, creates recovery thread at assess_injury step - run_recovery_takeover_respond(): 3-step flow: assess_injury → monitor_recovery (loop) → evaluate_and_return (DEP-C3-002 fused node) → handoff - Handoff: non-onboarding → baseline_pending_confirmation; onboarding → returns previous_journey_phase for CoordinatorService routing - 7 tests Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- check_hrv_trend(): sets recovery_flag after 3 consecutive degraded days - check_energy_patterns(): wraps detect_energy_patterns(), sets flags, 0 LLM - check_proactive_message_allowed(): enforces ≤2/week cap - check_baseline_exit_conditions(): triggers followup_transition at required weeks - run_daily_checks(): APScheduler daily entry point - 10 tests Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- test_phase_d_flows.py: 6 flows (HC direct, specialist chain, injury→takeover, onboarding 6 blocs, injury mid-onboarding suspend/resume, followup transition) - test_phase_d_clinical.py: 3 clinical flows (tca escalation, self_harm, clinical flag injected into specialist chain) - All LLM calls mocked; no real Anthropic API required - 9 tests passing Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… api-client chat module - packages/api-client/src/chat.ts: createChatClient() with sendMessage + getHistory - packages/ui-web: ChatBubble (role/content/specialists/timestamp) + TappableOptions (disappears after selection) - apps/web/src/app/chat/page.tsx: full chat page with ProtectedRoute, typing indicator, clarification axes - 7 Vitest tests (ChatBubble ×4, TappableOptions ×3) — 33/33 passing - Fixed pre-existing ThemeProvider.tsx unused @ts-expect-error directive Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…onboarding api-client - packages/api-client/src/onboarding.ts: createOnboardingClient() with start() + respond() - apps/web/src/app/onboarding/coach/page.tsx: 6-bloc stepper, resume detection, chat-style Q&A, completion → dashboard redirect - 5 Vitest tests for createOnboardingClient — 38/38 passing Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- data/classify_intent_eval.jsonl: 24 eval cases (HEAD_COACH_DIRECT, SPECIALIST_TECHNICAL, CLINICAL, OUT_OF_SCOPE, CLARIFICATION_NEEDED, EN/mixed) - scripts/eval_classify_intent.py: offline eval runner, 75% pass threshold, per-case PASS/FAIL + accuracy report - docs/backend/PHASE-D-AUDIT.md: full D1-D15 deliverable audit, architecture decisions, known limitations - .gitignore: allowlist eval dataset + eval script Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
Phase D adds a full conversational coaching layer on top of the frozen V1 backend. 17 commits across 15 sessions deliver: intent classification (Haiku 4.5 gateway), a chat-turn graph with Head Coach direct + specialist chain routing (up to 3 specialists), clinical escalation, onboarding Q&A (6 HITL blocs), followup transition, recovery takeover overlay, a proactive monitoring service, frontend chat UI + onboarding coach flow, an E2E test suite, and an offline eval dataset for `classify_intent`.
Why
Phase D is the first user-facing conversational surface of Resilio+. It enables athletes to interact with their Head Coach via chat (free messages, injury reports, session logging), and guides new athletes through a structured 6-bloc onboarding. All changes are additive — zero V1-FROZEN backend files were modified.
Deliverables
How to verify
```bash
Backend unit tests
poetry run pytest tests/backend/ -q --tb=short
Phase D E2E flows (mocked LLM)
poetry run pytest tests/e2e/test_phase_d_flows.py tests/e2e/test_phase_d_clinical.py -v
Frontend tests + typecheck
pnpm --filter @resilio/web test --run
pnpm --filter @resilio/web typecheck
classify_intent offline eval (requires ANTHROPIC_API_KEY)
python scripts/eval_classify_intent.py --verbose
```
Notable decisions
Audit reference
Full D1–D15 deliverable matrix with architecture decisions and known limitations: `docs/backend/PHASE-D-AUDIT.md`
🤖 Generated with Claude Code