Files
impactflow_discovery/tests/test_static_discovery.py
T
Joel Salmon b4d8d17aed 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>
2026-06-16 20:58:45 -05:00

57 lines
2.0 KiB
Python

from pathlib import Path
def test_discovery_page_uses_cookie_session_not_user_id():
"""The browser session is a server-set cookie, so the page must not mint
or carry a client-side user id, and all calls go through authedFetch."""
html = Path("app/static/discovery.html").read_text(encoding="utf-8")
assert "impactflow_user_id" not in html
assert "createUserId" not in html
assert "user_id" not in html
assert "authedFetch" in html
assert '/static/auth.js' in html
def test_profile_page_uses_me_endpoints_and_authed_fetch():
"""Profile reads/confirms via the user-scoped /me routes, authenticated by
the session cookie through authedFetch — not the old user_id-in-URL paths."""
html = Path("app/static/profile.html").read_text(encoding="utf-8")
assert "/discovery/profile/me" in html
assert "/discovery/profile/me/confirm" in html
assert "user_id" not in html
assert "authedFetch" in html
def test_profile_page_has_edit_affordance():
"""The read/edit/affirm DoD: an Edit control that PATCHes the profile."""
html = Path("app/static/profile.html").read_text(encoding="utf-8")
assert "renderEdit" in html
assert 'method: "PATCH"' in html
assert "Edit my words" in html
def test_reflect_page_uses_reflection_endpoints():
"""Phase 2 reflection page drives the coach loop via authedFetch."""
html = Path("app/static/reflect.html").read_text(encoding="utf-8")
assert "/discovery/profile/me/reflect" in html
assert "/discovery/profile/me/reflection" in html
assert "authedFetch" in html
assert "user_id" not in html
def test_profile_page_links_to_reflection():
html = Path("app/static/profile.html").read_text(encoding="utf-8")
assert "/static/reflect.html" in html
def test_auth_helper_sends_credentials_and_refreshes():
js = Path("app/static/auth.js").read_text(encoding="utf-8")
assert 'credentials: "include"' in js
assert "/api/auth/refresh" in js
assert "/api/auth/login" in js