mirror of
https://github.com/computerim/impactflow-discovery.git
synced 2026-08-27 04:50:35 +00:00
50453901b3
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>
37 lines
1.3 KiB
Python
37 lines
1.3 KiB
Python
"""Unit tests for the deterministic coaching-preference generator."""
|
|
from app.services.coaching import ALLOWED, generate_preferences
|
|
|
|
|
|
def test_gut_defaults_are_direct_and_high_friction():
|
|
p = generate_preferences({"triad": "gut"})
|
|
assert p["coaching_style"] == "direct"
|
|
assert p["friction_tolerance"] == "high"
|
|
assert p["prefer_questions_over_directives"] is False
|
|
|
|
|
|
def test_head_defaults_are_reflective_and_questions():
|
|
p = generate_preferences({"triad": "head"})
|
|
assert p["coaching_style"] == "reflective"
|
|
assert p["coaching_frequency"] == "biweekly"
|
|
assert p["prefer_questions_over_directives"] is True
|
|
|
|
|
|
def test_heart_defaults_are_warm_low_threshold():
|
|
p = generate_preferences({"triad": "heart"})
|
|
assert p["coaching_style"] == "warm"
|
|
assert p["misalignment_threshold"] == "low"
|
|
|
|
|
|
def test_unknown_triad_uses_gentle_fallback():
|
|
p = generate_preferences({"triad": None})
|
|
assert p["coaching_style"] == "warm"
|
|
assert p["prefer_questions_over_directives"] is True
|
|
|
|
|
|
def test_all_generated_values_are_within_allowed_sets():
|
|
for triad in ("gut", "heart", "head", None, "weird"):
|
|
p = generate_preferences({"triad": triad})
|
|
for field, allowed in ALLOWED.items():
|
|
assert p[field] in allowed, (triad, field, p[field])
|
|
assert isinstance(p["prefer_questions_over_directives"], bool)
|