feat: journal closeout for profile observations + LLM tool-use fixes #49
Reference in New Issue
Block a user
Delete Branch "dev"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
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_contextstill injectslearned_summaryinto journal-prep and chat). Newservices/journal_closeout.pyruns once per user per day atday_rollover_hour: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.background_modelsetting so the chat-model KV cache stays warm.closeout_enabledconfig key (default ON), wired to a Settings → Profile toggle.GET /api/profile/observationsendpoint + collapsible "Recent observations (14)" panel in Settings → Profile for transparency.observations_rawhas 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:
search_projectsreturnssuccess: True— was missing this key, so the dispatcher labeled valid results asstatus: errorand the model second-guessed itself.list_notesstrips type-nouns (task,note,project) fromqbefore ILIKE; thesearch_notestool description tells the LLM to usetype/projectparameters instead. Recovers literal-title hits when users say "find the X task".score_project_matchhelper with substring-first tiering (1.0 / 0.85 / 0.70 / SequenceMatcher-on-title) shared betweensearch_projects_toolandresolve_project. Stripsproject/projectsfiller words from the query so "famous supply project" substring-matches "Famous-Supply Work topics".record_momentdescription now requirestask_titlesto come from a priorsearch_notescall.JOURNAL_CALIBRATION(journal_pipeline.py) and the chattool_linesstatic 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/skipstests/test_tool_use_fixes.py—_strip_type_nounsvariants,score_project_matchtiers,search_projects_toolranks substring above SequenceMatcher,resolve_projectfinds colloquial matchManual verification on remote dev:
search_projectsshowsstatus: success, score 1.0 for exact-match substring;search_notescorrectly splitquery="tool-use"/type="task"and returned task #164🤖 Generated with Claude Code