mirror of
https://github.com/computerim/impactflow-discovery.git
synced 2026-08-27 07:10:37 +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:
@@ -188,3 +188,61 @@ class ProfileRevision(Base):
|
||||
fields_json: Mapped[str] = mapped_column(Text, nullable=False)
|
||||
note: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
|
||||
|
||||
|
||||
class CoachingPreferences(Base):
|
||||
"""Phase 3: how this person wants to be coached. Auto-generated from their
|
||||
Enneagram/Ikigai profile, then overridable. One row per user."""
|
||||
|
||||
__tablename__ = "coaching_preferences"
|
||||
|
||||
id: Mapped[str] = mapped_column(String, primary_key=True)
|
||||
user_id: Mapped[str] = mapped_column(
|
||||
String, ForeignKey("users.id"), unique=True, nullable=False, index=True
|
||||
)
|
||||
# The profile the defaults were derived from (provenance).
|
||||
profile_id: Mapped[Optional[str]] = mapped_column(
|
||||
String, ForeignKey("discovery_profile.id"), nullable=True
|
||||
)
|
||||
|
||||
coaching_frequency: Mapped[str] = mapped_column(String, nullable=False)
|
||||
coaching_style: Mapped[str] = mapped_column(String, nullable=False)
|
||||
misalignment_threshold: Mapped[str] = mapped_column(String, nullable=False)
|
||||
friction_tolerance: Mapped[str] = mapped_column(String, nullable=False)
|
||||
prefer_questions_over_directives: Mapped[bool] = mapped_column(
|
||||
Boolean, nullable=False, default=True
|
||||
)
|
||||
time_of_day_preference: Mapped[str] = mapped_column(String, nullable=False)
|
||||
|
||||
# True while still using auto-derived defaults; False once the user edits.
|
||||
auto_generated: Mapped[bool] = mapped_column(
|
||||
Boolean, nullable=False, default=True
|
||||
)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
|
||||
updated_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
|
||||
|
||||
|
||||
class CoachingCheckin(Base):
|
||||
"""Phase 3: a periodic coaching check-in. The body quotes the person's own
|
||||
words and asks whether their stated direction still feels valid (mirror,
|
||||
not compass). The person's answer is recorded in still_valid."""
|
||||
|
||||
__tablename__ = "coaching_checkin"
|
||||
|
||||
id: Mapped[str] = mapped_column(String, primary_key=True)
|
||||
user_id: Mapped[str] = mapped_column(
|
||||
String, ForeignKey("users.id"), nullable=False, index=True
|
||||
)
|
||||
profile_id: Mapped[str] = mapped_column(
|
||||
String, ForeignKey("discovery_profile.id"), nullable=False
|
||||
)
|
||||
body: Mapped[str] = mapped_column(Text, nullable=False)
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime, nullable=False, index=True
|
||||
)
|
||||
# The person's self-assessment: is their direction still valid?
|
||||
still_valid: Mapped[Optional[bool]] = mapped_column(Boolean, nullable=True)
|
||||
response_note: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
|
||||
acknowledged_at: Mapped[Optional[datetime]] = mapped_column(
|
||||
DateTime, nullable=True
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user