mirror of
https://github.com/computerim/impactflow-discovery.git
synced 2026-08-27 07:30:35 +00:00
Phase 2: AI coach reflection loop
Add the mirror-not-compass reflection layer between profile generation and
affirmation. The coach reflects the person's profile back, and only when they
explicitly correct or add something does it propose revisions in their own
direction — never prescribing goals.
- ReflectionCoach service (app/services/reflector.py): Anthropic-backed,
returns {message, revisions, revision_note}; revisions filtered to the seven
editable prose fields (never triad/type); one-retry JSON handling.
- Endpoints (owner-scoped, 409 when locked): POST /discovery/profile/me/reflect
(opener + turns, applies revisions), GET .../reflection (dialogue),
GET .../revisions (iteration history). complete records an 'extraction'
revision; PATCH records 'manual_edit'.
- Models + migration 004: reflection_message (coach/person turns) and
profile_revision (snapshots: extraction | reflection | manual_edit) —
captures edits and iterations rather than overwriting.
- Frontend: reflect.html chat (coach/person bubbles, live profile summary that
refreshes on revision, affirm); linked from profile.html.
- Affirmation remains the existing confirm/lock.
Also refresh README for Phase 2 and for the HTTPS deployment
(https://impactflow.teamci.org:8011, OAUTH_REDIRECT_URI + COOKIE_SECURE notes).
Tests: 50 passing (added reflector unit tests and reflection endpoint tests;
run in-container).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
"""add reflection_message and profile_revision (Phase 2 AI coach loop)
|
||||
|
||||
Revision ID: 004
|
||||
Revises: 003
|
||||
Create Date: 2026-06-16
|
||||
|
||||
Phase 2: the AI-coach reflection loop stores its dialogue in reflection_message,
|
||||
and every change to a profile's prose (initial extraction, reflection-applied
|
||||
revision, or manual edit) is snapshotted in profile_revision.
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
revision: str = "004"
|
||||
down_revision: Union[str, None] = "003"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.create_table(
|
||||
"reflection_message",
|
||||
sa.Column("id", sa.String(), primary_key=True),
|
||||
sa.Column(
|
||||
"profile_id",
|
||||
sa.String(),
|
||||
sa.ForeignKey("discovery_profile.id"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column(
|
||||
"user_id",
|
||||
sa.String(),
|
||||
sa.ForeignKey("users.id"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("role", sa.String(), nullable=False),
|
||||
sa.Column("content", sa.Text(), nullable=False),
|
||||
sa.Column("sequence", sa.Integer(), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||
)
|
||||
op.create_index(
|
||||
"ix_reflection_message_profile_id",
|
||||
"reflection_message",
|
||||
["profile_id"],
|
||||
)
|
||||
op.create_index(
|
||||
"ix_reflection_message_user_id", "reflection_message", ["user_id"]
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"profile_revision",
|
||||
sa.Column("id", sa.String(), primary_key=True),
|
||||
sa.Column(
|
||||
"profile_id",
|
||||
sa.String(),
|
||||
sa.ForeignKey("discovery_profile.id"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column(
|
||||
"user_id",
|
||||
sa.String(),
|
||||
sa.ForeignKey("users.id"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.Column("source", sa.String(), nullable=False),
|
||||
sa.Column("fields_json", sa.Text(), nullable=False),
|
||||
sa.Column("note", sa.Text(), nullable=True),
|
||||
sa.Column("created_at", sa.DateTime(), nullable=False),
|
||||
)
|
||||
op.create_index(
|
||||
"ix_profile_revision_profile_id", "profile_revision", ["profile_id"]
|
||||
)
|
||||
op.create_index(
|
||||
"ix_profile_revision_user_id", "profile_revision", ["user_id"]
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_index(
|
||||
"ix_profile_revision_user_id", table_name="profile_revision"
|
||||
)
|
||||
op.drop_index(
|
||||
"ix_profile_revision_profile_id", table_name="profile_revision"
|
||||
)
|
||||
op.drop_table("profile_revision")
|
||||
|
||||
op.drop_index(
|
||||
"ix_reflection_message_user_id", table_name="reflection_message"
|
||||
)
|
||||
op.drop_index(
|
||||
"ix_reflection_message_profile_id", table_name="reflection_message"
|
||||
)
|
||||
op.drop_table("reflection_message")
|
||||
Reference in New Issue
Block a user