mirror of
https://github.com/computerim/impactflow-discovery.git
synced 2026-08-27 06:20:36 +00:00
Phase 3: coaching preferences + weekly check-in engine
Add coaching preferences (auto-derived from the profile, user-overridable) and a periodic check-in engine that quotes the person's own words and asks whether their direction still feels valid — mirror, not compass. - Preferences are deterministic: a documented triad mapping (gut → direct/ higher-friction, heart → warm/drift-sensitive, head → reflective/question-led) produces defaults for the six fields (coaching_frequency, coaching_style, misalignment_threshold, friction_tolerance, prefer_questions_over_directives, time_of_day_preference). PUT overrides; regenerate re-derives. - CheckinCoach (app/services/coaching.py): Anthropic-backed; writes a check-in that quotes the person's goals back and asks if the direction still holds. - Endpoints (app/routers/coaching.py): GET/PUT/regenerate preferences; GET/POST checkins; respond (records still_valid); admin POST /run is the weekly batch (due = cadence elapsed + locked profile), intended for a cron. - Models + migration 005: coaching_preferences (per user) and coaching_checkin. - Frontend: coaching.html (preferences form + check-in feed); linked from profile.html. Tests: 68 passing (added deterministic-preference unit tests and coaching endpoint/batch tests; run in-container). README updated for Phase 3. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -42,6 +42,10 @@ If you need to explain this app in detail, use this mental model:
|
||||
profile through an AI-coach reflection loop (`POST /discovery/profile/me/reflect`)
|
||||
— the coach mirrors the profile back and applies the person's own
|
||||
corrections — before affirming with the same confirm/lock.
|
||||
13. After affirming (Phase 3) the user opens `/static/coaching.html` to tune
|
||||
coaching preferences (auto-derived from their profile) and receive periodic
|
||||
check-ins that quote their own words and ask if their direction still holds.
|
||||
A weekly cron calls `POST /discovery/coaching/run` to generate due check-ins.
|
||||
|
||||
Machine-to-machine callers (e.g. the MCP server) skip the OAuth dance and
|
||||
authenticate with `X-API-Key: $IMPACTFLOW_API_KEY` instead. That header
|
||||
@@ -142,16 +146,22 @@ Important files:
|
||||
| `app/static/discovery.html` | Browser-based seven-prompt flow |
|
||||
| `app/static/profile.html` | Browser-based profile display, edit, and confirm actions; links to reflection |
|
||||
| `app/static/reflect.html` | Phase 2 AI-coach reflection chat (mirror loop, applies revisions, affirm) |
|
||||
| `app/static/coaching.html` | Phase 3 coaching preferences form + check-in feed |
|
||||
| `app/static/auth.js` | Shared `authedFetch` helper: sends session cookies, silently refreshes on `401`, redirects to login |
|
||||
| `app/static/style.css` | Shared UI styling |
|
||||
| `app/services/reflector.py` | `ReflectionCoach`: Anthropic-backed mirror loop, JSON parsing, revision filtering |
|
||||
| `app/services/coaching.py` | Deterministic preference generator + `CheckinCoach` (Anthropic check-in text) |
|
||||
| `app/routers/coaching.py` | Phase 3 coaching routes: preferences, check-ins, weekly batch `/run` |
|
||||
| `alembic/versions/001_initial.py` | Initial database schema migration |
|
||||
| `alembic/versions/002_add_auth.py` | Adds `users`, `refresh_tokens`, and `activity_log` tables |
|
||||
| `alembic/versions/003_add_goals.py` | Adds the goal columns to `discovery_conversation` and `discovery_profile` |
|
||||
| `alembic/versions/004_add_reflection.py` | Adds `reflection_message` and `profile_revision` tables (Phase 2) |
|
||||
| `alembic/versions/005_add_coaching.py` | Adds `coaching_preferences` and `coaching_checkin` tables (Phase 3) |
|
||||
| `tests/conftest.py` | Shared `app_client` fixture (isolated app + temp DB) |
|
||||
| `tests/test_extractor.py` | Unit tests for extraction plumbing, goals, and retry behavior |
|
||||
| `tests/test_reflector.py` | Unit tests for `ReflectionCoach` (mirror, revision filtering, retry) |
|
||||
| `tests/test_coaching_prefs.py` | Unit tests for the deterministic coaching-preference generator |
|
||||
| `tests/test_coaching.py` | Tests for coaching endpoints (preferences, check-ins, due-logic batch) |
|
||||
| `tests/test_auth.py` | Tests for the dual-auth dependency (JWT + cookie + API key), token refresh/logout, admin enforcement, and domain allow-list |
|
||||
| `tests/test_profile_edit.py` | Tests for `PATCH /discovery/profile/me` (edit, partial update, lock/`409`) |
|
||||
| `tests/test_reflection.py` | Tests for the reflection endpoints (turns, applied revisions, lock/`409`, history) |
|
||||
@@ -386,6 +396,37 @@ still the `/confirm` lock above; the loop is what happens before it.
|
||||
history, newest first. Every change is snapshotted in `profile_revision`
|
||||
with a `source` of `extraction` (initial), `reflection`, or `manual_edit`.
|
||||
|
||||
### 9. Coaching Preferences & Check-ins (Phase 3)
|
||||
|
||||
How the person wants to be coached, plus a periodic check-in engine.
|
||||
|
||||
Coaching **preferences** are auto-generated from the profile's Enneagram centre
|
||||
(a deterministic mapping — gut → direct/higher-friction, heart → warm/sensitive
|
||||
to drift, head → reflective/question-led) and are fully overridable. Fields:
|
||||
`coaching_frequency`, `coaching_style`, `misalignment_threshold`,
|
||||
`friction_tolerance`, `prefer_questions_over_directives`,
|
||||
`time_of_day_preference`.
|
||||
|
||||
- `GET /discovery/coaching/preferences` returns the preferences, deriving
|
||||
defaults from the latest profile on first access (`404` if no profile yet).
|
||||
- `PUT /discovery/coaching/preferences` overrides any field (validated against
|
||||
the allowed value sets) and marks them user-customized.
|
||||
- `POST /discovery/coaching/preferences/regenerate` re-derives the defaults
|
||||
from the latest profile, discarding overrides.
|
||||
|
||||
A **check-in** quotes the person's own words and asks whether their stated
|
||||
direction still feels valid — mirror, not compass: it asks, it never judges or
|
||||
prescribes. The person's answer is recorded in `still_valid`.
|
||||
|
||||
- `POST /discovery/coaching/checkins` generates a check-in now (on demand).
|
||||
- `GET /discovery/coaching/checkins` lists them, newest first.
|
||||
- `PUT /discovery/coaching/checkins/{id}/respond` records the self-assessment
|
||||
(`still_valid` + optional note).
|
||||
- `POST /discovery/coaching/run` is the **weekly batch job** (admin-only,
|
||||
intended for a cron): it generates a check-in for every eligible user whose
|
||||
cadence is due. Eligible = coaching cadence not `off` and an affirmed
|
||||
(locked) profile; due = no prior check-in or the cadence interval has elapsed.
|
||||
|
||||
## API Reference
|
||||
|
||||
All `/discovery/*`, `/api/me*`, `/api/activity*`, and `/api/admin/*` routes
|
||||
@@ -421,6 +462,13 @@ clients.) `/api/auth/login`, `/api/auth/callback`, `/health`, `/`, and
|
||||
| `GET` | `/discovery/profile/me/reflection` | yes | The reflection dialogue for the latest profile |
|
||||
| `GET` | `/discovery/profile/me/revisions` | yes | Profile edit/iteration history (newest first) |
|
||||
| `GET` | `/discovery/conversation/{conversation_id}` | yes | Fetch stored conversation responses (owner only) |
|
||||
| `GET` | `/discovery/coaching/preferences` | yes | Coaching preferences (auto-derived on first access) |
|
||||
| `PUT` | `/discovery/coaching/preferences` | yes | Override coaching preferences |
|
||||
| `POST` | `/discovery/coaching/preferences/regenerate` | yes | Re-derive preference defaults from the profile |
|
||||
| `GET` | `/discovery/coaching/checkins` | yes | List the user's check-ins (newest first) |
|
||||
| `POST` | `/discovery/coaching/checkins` | yes | Generate a check-in now |
|
||||
| `PUT` | `/discovery/coaching/checkins/{id}/respond` | yes | Record "is your direction still valid?" |
|
||||
| `POST` | `/discovery/coaching/run` | admin | Weekly batch: generate due check-ins for eligible users |
|
||||
|
||||
## Data Model
|
||||
|
||||
@@ -541,6 +589,39 @@ iterations are captured rather than overwritten (migration `004`).
|
||||
| `note` | text nullable | What changed (e.g. the coach's revision note) |
|
||||
| `created_at` | datetime | UTC |
|
||||
|
||||
### `coaching_preferences`
|
||||
|
||||
How the person wants to be coached; one row per user (migration `005`).
|
||||
|
||||
| Column | Type | Notes |
|
||||
| --- | --- | --- |
|
||||
| `id` | string | UUID primary key |
|
||||
| `user_id` | string | FK to `users.id`, unique, indexed |
|
||||
| `profile_id` | string nullable | The profile the defaults were derived from |
|
||||
| `coaching_frequency` | string | `weekly`, `biweekly`, `monthly`, or `off` |
|
||||
| `coaching_style` | string | `direct`, `warm`, or `reflective` |
|
||||
| `misalignment_threshold` | string | `low`, `medium`, or `high` |
|
||||
| `friction_tolerance` | string | `low`, `medium`, or `high` |
|
||||
| `prefer_questions_over_directives` | boolean | Lead with questions vs statements |
|
||||
| `time_of_day_preference` | string | `morning`, `afternoon`, or `evening` |
|
||||
| `auto_generated` | boolean | True until the user edits a field |
|
||||
| `created_at` / `updated_at` | datetime | UTC |
|
||||
|
||||
### `coaching_checkin`
|
||||
|
||||
A periodic coaching check-in and the person's response (migration `005`).
|
||||
|
||||
| Column | Type | Notes |
|
||||
| --- | --- | --- |
|
||||
| `id` | string | UUID primary key |
|
||||
| `user_id` | string | FK to `users.id`, indexed |
|
||||
| `profile_id` | string | FK to `discovery_profile.id` |
|
||||
| `body` | text | The check-in text (quotes the person's own words) |
|
||||
| `created_at` | datetime | UTC, indexed |
|
||||
| `still_valid` | boolean nullable | The person's answer: is their direction still valid? |
|
||||
| `response_note` | text nullable | Optional note with their response |
|
||||
| `acknowledged_at` | datetime nullable | When they responded |
|
||||
|
||||
## Extraction Details
|
||||
|
||||
`DiscoveryExtractor` is intentionally responsible for plumbing, not business
|
||||
@@ -626,6 +707,17 @@ callback), so the pages hold no tokens of their own.
|
||||
true it updates the summary and notes what changed
|
||||
- "This is me — affirm" locks the profile via the same confirm endpoint
|
||||
|
||||
`coaching.html` (Phase 3 coaching):
|
||||
|
||||
- loads preferences via `GET /discovery/coaching/preferences` (auto-derived on
|
||||
first visit) and renders the six fields as selects + a toggle; Save
|
||||
(`PUT`) marks them customized, "Reset to suggested" re-derives from the
|
||||
profile
|
||||
- lists check-ins and can generate one on demand (`POST .../checkins`); each
|
||||
unanswered check-in offers "still feels true" / "it's shifted" which posts to
|
||||
`.../respond`
|
||||
- linked from `profile.html` ("Coaching preferences & check-ins")
|
||||
|
||||
## Configuration
|
||||
|
||||
Populate `.env` with at minimum the Anthropic key and the auth-related
|
||||
|
||||
Reference in New Issue
Block a user