Files
FabledScribe/alembic/versions/0049_conversation_curator_summary.py
bvandeusen fa97ade8e3 feat(journal): curator summary feeds back into chat context (Phase 3)
The architecture loop closes. Curator extracts beats and writes a
≤240-char summary; the next chat turn loads that summary into the
journal system prompt so the chat model — which has no tools and
cannot retrieve anything itself — gains awareness of recent topics
captured by the curator.

Migration 0049:
- conversations.curator_summary (text, nullable). Last-write-wins; no
  history of prior summaries.

models/conversation.py:
- New curator_summary column on Conversation.

services/curator_scheduler.py:
- _stamp_last_run() takes an optional summary kwarg; persists it when
  non-empty (clobbering the previous summary). Empty summary keeps
  the existing one rather than overwriting useful context with "".
- _sweep() passes result.summary through.

routes/journal.py:
- Manual /api/journal/curator/run/<conv_id> writes curator_summary
  alongside last_curator_run_at on success.

services/journal_pipeline.py:
- build_journal_system_prompt() gains an optional `conv_id` param.
  When provided, appends a "CURATOR NOTES" block at the end of the
  system prompt with the conversation's stored summary. Positioned
  after ambient context so the chat model treats it as current
  awareness rather than background.

services/llm.py:
- Threads conv_id through to build_journal_system_prompt.

This is the last commit of the conversation+curator architecture
arc (Fable #172):
- Phase 1a (a7002a8): chat=tools[], curator service backend
- Phase 1b (a73dd17): right-rail captures panel + manual trigger
- Phase 2   (83f1676): auto-scheduler every 15 min
- Phase 3   (this): curator summary → chat context feedback loop

Operator can now device-test the architecture end-to-end: have a
journal conversation (model can't lie about tool calls because it
has none), wait for the scheduler or hit "Process captures", see
moments appear in the right rail, then continue the conversation
and notice the chat model staying topic-aware via the summary block.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 10:09:33 -04:00

37 lines
1.0 KiB
Python

"""conversations.curator_summary — feeds curator output back to the chat model
Revision ID: 0049
Revises: 0048
Create Date: 2026-05-22
Phase 3 of the conversation+curator architecture (Fable #172). The
curator's final summary line (≤ 240 chars) is persisted here so the
NEXT chat turn can include it in the system prompt. This is the
feedback loop that closes the architecture — without it, the chat
model has no awareness of what the curator extracted, and conversations
risk circling back to topics already captured.
NULL means "no curator pass has produced a summary yet." The chat
pipeline reads this column when building the journal system prompt;
missing column = no injection, no failure.
"""
from alembic import op
import sqlalchemy as sa
revision = "0049"
down_revision = "0048"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.add_column(
"conversations",
sa.Column("curator_summary", sa.Text, nullable=True),
)
def downgrade() -> None:
op.drop_column("conversations", "curator_summary")