-
released this
2026-05-12 21:32:01 -04:00 | 260 commits to main since this releaseTwo threads — one rebuilds a half-deleted feature, the other patches an LLM-misuse failure mode caught in prod.
Journal closeout for profile observations
The briefing tear-down on 2026-04-25 deleted the closeout job but left the read path:
build_profile_contextstill injectslearned_summaryinto journal-prep and chat system prompts. Result was a stale frozen summary plus no path to refresh it. This release reinstates the write side with a hard guard against the briefing-era feedback loop.services/journal_closeout.py(new) — runs once per user per day at the configuredday_rollover_hour. Reads yesterday's/journalconversation, filters out messages withmsg_metadata.kind="daily_prep"at the SQL layer, asks the background LLM for 2–5 bullet observations focused on user-side patterns (NOT structured fields like name/job/expertise — those are still theupdate_profiletool's lane), and appends toobservations_rawvia the existingappend_observations.- Hard data-exclusion guard — the daily-prep block is structurally invisible to the closeout LLM. The previous briefing-era closeout sent all messages with role labels and relied on the prompt to say "user revealed"; that's the shape that caused the feedback loop. Instruction-only role separation isn't reliable on local Ollama models against long transcripts.
background_modelfor the LLM call — keeps chat-model KV cache warm.closeout_enabledconfig key (default ON), wired to a Settings → Profile toggle. NewGET /api/profile/observationsendpoint and collapsible "Recent observations (14)" panel for transparency.- Catch-up on startup — if today's
day_rollover_houralready passed andobservations_rawhas no entry for yesterday, runs once.
LLM tool-use fixes from 2026-05-08 journal session
Prod conversation #282 exposed three concrete code bugs and two behavioral patterns. All five fixes shipped together because they share a root failure mode: when the user names a project/task colloquially, the model fails to find it, fails to trust valid matches, or skips searching entirely and fabricates a moment.
search_projectsreturnssuccess: True— the dispatcher reads this key to setstatuson the conversation history entry. Missing key → result labeledstatus: erroreven when data was returned → model second-guessed valid matches. One-line fix.list_notesstrips type-nouns fromq—task,tasks,note,notes,project,projectsare filtered out of the keyword-search tokens before the AND-ILIKE filter is built. Closed the case where "find the sebring secondary task" returned 7 unrelated tasks because task #95's title and body don't contain the word "task".search_notestool description also tells the LLM to usetype/projectparameters instead of stuffing those words intoquery.score_project_matchshared helper (new intools/_helpers.py) — tiered scoring (1.0 exact title / 0.85 substring either way / 0.70 query-in-description / SequenceMatcher-against-title fallback). Stripsproject/projectsfiller from the query so "famous supply project" substring-matches "Famous-Supply Work topics" at 0.85 instead of 0.228 SequenceMatcher floor. Bothsearch_projects_toolandresolve_projectuse the helper — single source of truth, no drift between the LLM-facing surface and the create-task resolver.record_momentdescription —task_titles/note_titlesmust come from a priorsearch_notescall in the same turn. Prevents the project-name-as-task-title misuse that triggered the original investigation.- Search-first heuristic added to both
JOURNAL_CALIBRATIONand the chattool_linesstatic block: when the user references existing work, callsearch_notesfirst; create or record only after no match surfaces and the user confirms.
Verification
- 178+ backend pytest tests pass (CI)
tests/test_journal_closeout.py— filter, run_for_user happy + skip paths, scheduler register/disable, catch-uptests/test_tool_use_fixes.py—_strip_type_nouns,score_project_matchtiers,search_projects_toolranks substring above SequenceMatcher,resolve_projectfinds colloquial match- Tool-use fixes verified end-to-end against fable-dev MCP:
search_projectsshowsstatus: success, score 1.0 for exact-match substring;search_notescorrectly splitquery="tool-use"/type="task"and returned the right task - Search-first heuristic verified in journal UI
Deploy notes
- No schema migrations; deploy-only
- After deploy, optionally click Reset Learned Data in Settings → Profile to wipe the stale briefing-era
learned_summary. The new closeout will rebuild from clean observations starting tomorrow's prep cycle.
Out of scope (follow-ups)
- Manual verification of the closeout toggle / observations panel (PR test plan checkbox left unchecked — covered separately)
- Hybrid BM25 + embedding search (replaces semantic-only)
- "Did you mean X?" UI surface for project matches in the 0.40–0.85 score band
Downloads