feat: journal closeout for profile observations + LLM tool-use fixes #49

Merged
bvandeusen merged 0 commits from dev into main 2026-05-12 21:31:21 -04:00
bvandeusen commented 2026-05-12 21:27:24 -04:00 (Migrated from git.fabledsword.com)

Summary

Two independent threads bundled into this PR — both started from observed prod failures and ship together because they landed back-to-back on dev.

1. Journal closeout for profile observations (12 commits)

Rebuilds the write path for user_profiles.observations_raw / learned_summary. The briefing tear-down on 2026-04-25 deleted the closeout but left the read path (build_profile_context still injects learned_summary into journal-prep and chat). New services/journal_closeout.py runs once per user per day at day_rollover_hour:

  • Hard data-exclusion guard: messages with msg_metadata.kind="daily_prep" are filtered before they reach the LLM — this closes the briefing-era feedback loop where the prep block dominated attention and got attributed to the user.
  • Uses the background_model setting so the chat-model KV cache stays warm.
  • closeout_enabled config key (default ON), wired to a Settings → Profile toggle.
  • New GET /api/profile/observations endpoint + collapsible "Recent observations (14)" panel in Settings → Profile for transparency.
  • Catch-up on startup: if today's slot already passed and observations_raw has no entry for yesterday, runs once.

2. LLM tool-use fixes from 2026-05-08 journal session (11 commits)

Three code bugs + two behavioral patches identified in prod conversation #282:

  • Bug A: search_projects returns success: True — was missing this key, so the dispatcher labeled valid results as status: error and the model second-guessed itself.
  • Bug B: list_notes strips type-nouns (task, note, project) from q before ILIKE; the search_notes tool description tells the LLM to use type / project parameters instead. Recovers literal-title hits when users say "find the X task".
  • Bug C: New score_project_match helper with substring-first tiering (1.0 / 0.85 / 0.70 / SequenceMatcher-on-title) shared between search_projects_tool and resolve_project. Strips project / projects filler words from the query so "famous supply project" substring-matches "Famous-Supply Work topics".
  • record_moment description now requires task_titles to come from a prior search_notes call.
  • Search-first heuristic added to both JOURNAL_CALIBRATION (journal_pipeline.py) and the chat tool_lines static block (llm.py): when the user references existing work, search first; only record a moment / create a task if nothing matches and the user confirms.

Fable-dev task #164 tracks Section 2.

Test plan

Backend unit tests (covered by CI):

  • tests/test_journal_closeout.py — filter excludes daily_prep, run_for_user happy + skip paths, scheduler register/disable, catch-up runs/skips
  • tests/test_tool_use_fixes.py_strip_type_nouns variants, score_project_match tiers, search_projects_tool ranks substring above SequenceMatcher, resolve_project finds colloquial match

Manual verification on remote dev:

  • Tool-use fixes — verified via fable-dev MCP conv #299/300/301: search_projects shows status: success, score 1.0 for exact-match substring; search_notes correctly split query="tool-use" / type="task" and returned task #164
  • Search-first heuristic — verified in journal UI by user
  • Closeout — toggle off in Settings → Profile, confirm cron removed; toggle on, force a closeout, confirm bullets contain no prep-block content; "Recent observations" panel renders entries

🤖 Generated with Claude Code

## Summary Two independent threads bundled into this PR — both started from observed prod failures and ship together because they landed back-to-back on `dev`. ### 1. Journal closeout for profile observations (12 commits) Rebuilds the write path for `user_profiles.observations_raw` / `learned_summary`. The briefing tear-down on 2026-04-25 deleted the closeout but left the read path (`build_profile_context` still injects `learned_summary` into journal-prep and chat). New `services/journal_closeout.py` runs once per user per day at `day_rollover_hour`: - **Hard data-exclusion guard:** messages with `msg_metadata.kind="daily_prep"` are filtered before they reach the LLM — this closes the briefing-era feedback loop where the prep block dominated attention and got attributed to the user. - Uses the `background_model` setting so the chat-model KV cache stays warm. - `closeout_enabled` config key (default ON), wired to a Settings → Profile toggle. - New `GET /api/profile/observations` endpoint + collapsible "Recent observations (14)" panel in Settings → Profile for transparency. - Catch-up on startup: if today's slot already passed and `observations_raw` has no entry for yesterday, runs once. ### 2. LLM tool-use fixes from 2026-05-08 journal session (11 commits) Three code bugs + two behavioral patches identified in prod conversation #282: - **Bug A:** `search_projects` returns `success: True` — was missing this key, so the dispatcher labeled valid results as `status: error` and the model second-guessed itself. - **Bug B:** `list_notes` strips type-nouns (`task`, `note`, `project`) from `q` before ILIKE; the `search_notes` tool description tells the LLM to use `type` / `project` parameters instead. Recovers literal-title hits when users say "find the X task". - **Bug C:** New `score_project_match` helper with substring-first tiering (1.0 / 0.85 / 0.70 / SequenceMatcher-on-title) shared between `search_projects_tool` and `resolve_project`. Strips `project` / `projects` filler words from the query so "famous supply project" substring-matches "Famous-Supply Work topics". - **`record_moment` description** now requires `task_titles` to come from a prior `search_notes` call. - **Search-first heuristic** added to both `JOURNAL_CALIBRATION` (`journal_pipeline.py`) and the chat `tool_lines` static block (`llm.py`): when the user references existing work, search first; only record a moment / create a task if nothing matches and the user confirms. Fable-dev task #164 tracks Section 2. ## Test plan Backend unit tests (covered by CI): - [x] `tests/test_journal_closeout.py` — filter excludes daily_prep, run_for_user happy + skip paths, scheduler register/disable, catch-up runs/skips - [x] `tests/test_tool_use_fixes.py` — `_strip_type_nouns` variants, `score_project_match` tiers, `search_projects_tool` ranks substring above SequenceMatcher, `resolve_project` finds colloquial match Manual verification on remote dev: - [x] Tool-use fixes — verified via fable-dev MCP conv #299/300/301: `search_projects` shows `status: success`, score 1.0 for exact-match substring; `search_notes` correctly split `query="tool-use"` / `type="task"` and returned task #164 - [x] Search-first heuristic — verified in journal UI by user - [ ] Closeout — toggle off in Settings → Profile, confirm cron removed; toggle on, force a closeout, confirm bullets contain no prep-block content; "Recent observations" panel renders entries 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: bvandeusen/FabledScribe#49