# Changelog Development history — most recent first. --- ## 2026-03-24 — fable-mcp serve, admin log tool, Settings MCP install UI - **Dockerfile** — builds `fable-mcp` wheel into `/app/dist/` at image build time - **`routes/fable_mcp_dist.py`** — `GET /api/fable-mcp/info` (availability check) and `GET /api/fable-mcp/download` (wheel download) - **`fable_mcp/tools/admin.py`** — `get_app_logs()` calling `/api/admin/logs` - **`fable_mcp/server.py`** — `fable_get_app_logs` MCP tool - **SettingsView** — "Fable MCP" section in API Keys tab: wheel download button and install instructions - **ci.yml** — `fable-mcp/**` added to trigger paths ## 2026-03-23 — fable-mcp scaffold + all tools - `fable-mcp/` Python package scaffolded (`pyproject.toml`, `hatchling` build) - `FableClient` — async httpx wrapper with `FABLE_URL` + `FABLE_API_KEY` auth - Tool modules: `notes`, `tasks`, `projects`, `milestones`, `search`, `chat`, `admin` - `server.py` — FastMCP stdio transport; all tools registered - Settings UI — API Keys tab with create/revoke, `.env` download, Claude config download - `routes/api_keys.py` — `POST /api/api-keys`, `GET /api/api-keys`, `DELETE /api/api-keys/:id` ## 2026-03-23 — Model management UI, RSS category/refresh, weather city search - **SettingsView** — Model Management section: list installed models (size, loaded state), pull by name (SSE progress), delete unused - **`routes/chat.py` `list_models_route`** — also hits `/api/ps` in parallel; returns `loaded: bool` and `modified_at` per model - **RSS feeds** — category badge, last-fetched age, manual refresh button, category input on add - **`routes/briefing.py`** — `POST /api/briefing/feeds/refresh` triggers immediate re-fetch - **`get_weather` tool** — now accepts any city name (geocodes via Nominatim + fetches Open-Meteo); not limited to pre-configured locations ## 2026-03-22 — Briefing scheduler fix, quick capture fix, task back navigation - **Briefing scheduler** — fixed deadlock: `start_briefing_scheduler` made async; replaced `run_coroutine_threadsafe(...).result()` with `await` (was blocking the event loop thread) - **Briefing pipeline** — added early return when both LLM synthesis lanes produce empty strings (was always prepending "Good morning" on failure) - **Quick capture** — fixed: was always creating notes. Two causes fixed: (1) now uses user's `default_model` setting instead of `Config.OLLAMA_MODEL`; (2) removed `create_project` from classifier prompt (tool not offered, causing fallback to note) - **TaskViewerView** — back button navigates to task's project when `project_id` is set; Esc handler registered in capture phase to prevent conflict with App.vue global handler ## 2026-03-21 — Briefing RSS + project kanban advance buttons - **ProjectView kanban** — status-advance buttons on task cards (→ todo→in_progress, ✓ in_progress→done), hidden until hover - **Briefing UI** — RSS feeds section: add URL + category, list feeds with last-fetched age badge, delete, manual refresh ## 2026-03-16 — Fuzzy project resolution, create_project guard, task embedding fix - `services/tools.py` — `_resolve_project` rewritten with 4-step lookup: (1) exact DB match, (2) `query in title` substring, (3) `title in query` reverse substring, (4) SequenceMatcher ≥ 0.55. Partial names now reliably resolve without creating duplicates. - `services/tools.py` — `create_project` handler: similar-project checks (threshold 0.55) run **before** the `confirmed` gate, so `confirmed=true` cannot bypass them. - `routes/tasks.py` — fire-and-forget `upsert_note_embedding` added to create and update routes (tasks weren't getting embeddings via REST API). - `services/briefing_pipeline.py` — `await is_caldav_configured()` fix (was called without await — latent bug since the function became async). - `services/notes.py` — removed erroneous duplicate `count_query.where(Note.parent_id == parent_id)` that was applied inside the `no_project` branch. ## 2026-03-12 — CI release process update - `.forgejo/workflows/ci.yml` — removed `main` from `branches` push trigger. CI fires only on `dev` pushes, `v*` tags. Production release: create release via Forgejo UI on `main` with a `v*` tag → tag push fires CI → build job pushes `:latest` + `:`. ## 2026-03-11 — Daily Briefing (full feature) Backend: - Migration `0026_add_briefing_tables.py`: `rss_feeds`, `rss_items`, `weather_cache`; added `conversation_type` and `briefing_date` to `conversations`. - `services/weather.py`: geocoding via Nominatim, forecast via Open-Meteo, per-user DB cache. - `services/rss.py`: feedparser-based fetch, per-feed DB cache prune-to-100. - `services/briefing_pipeline.py`: two-lane parallel gather (weather, RSS, tasks, calendar) → LLM synthesis → GenerationBuffer SSE stream. - `services/briefing_scheduler.py` (APScheduler): slots morning/midday/evening/night with catch-up logic. - `routes/briefing.py`: RSS CRUD at `/api/briefing/feeds`, weather config, briefing config, `POST /api/briefing/trigger`. Frontend: - `BriefingView.vue`: primary briefing page; digest card + conversation thread + reply bar. - `BriefingSetupWizard.vue`: 4-step setup wizard shown on first visit. - SettingsView: Briefing tab (enable toggle, location geocoding, office days, time slot toggles, RSS CRUD). - AppHeader: "Briefing" nav link added. ## 2026-03-11 — Version footer, task history, note version retention - CalVer build-time version tracking: `Dockerfile` injects `BUILD_VERSION` ARG; displayed in Settings footer. - Task history moved to collapsible right sidebar (previously modal overlay). - Note version retention: configurable max versions per note (default 20) enforced at write time. - Content deduplication in `build_context()` — same note cannot appear in multiple context blocks. ## 2026-03-11 — Multi-user sharing, notifications, groups, backup rewrite - Migration `0025_add_sharing_and_notifications.py`: `groups`, `group_memberships`, `project_shares`, `note_shares`, `notifications`. - `services/access.py`: `PERMISSION_RANK` dict; `get_project_permission` / `get_note_permission` — check ownership, direct share, group-based share, note→project inheritance. - Share dialog (`ShareDialog.vue`): user tab (search + permission select), group tab, current shares list with inline change/remove. - `SharedWithMeView.vue`: `/shared` route — shared projects and notes. - In-app notification bell with 60s polling; push generated on share/group events. - Backup rewrite (version 2): includes all tables; `restore_full_backup` builds ID maps, two-pass note restore for `parent_id` patching. ## 2026-03-10 — Project Workspace, Graph View, settings tabs, message queue persistence - `WorkspaceView.vue`: 3-panel layout (tasks/chat/notes), collapse toggles, SSE watcher auto-loads notes and refreshes tasks. - `WorkspaceTaskPanel.vue`: milestone-grouped task list, detail slide-over with status cycle + log. - `WorkspaceNoteEditor.vue`: list ↔ TipTap editor, autosave. - `GraphView.vue`: D3 force-directed graph, tag nodes, project hub nodes, physics panel, peek panel. - SettingsView fully rewritten with tabbed layout (General, Account, Notifications, Integrations, Data, Admin). - Message queue: persisted to localStorage; queued bubbles shown in chat with opacity + "QUEUED" label. - ToolCallCard: confirm/deny inline; "Create anyway" posts directly to API (no LLM round-trip). - Duplicate detection: scoped to same `is_task` type; semantic threshold raised 0.87 → 0.90. ## 2026-03-10 — "Illuminated Transcript" design language - Fraunces font for headings; assistant bubbles elevated with left indigo accent + shadow; user bubbles ghosted (transparent bg, muted text). - Dark palette shifted to slate-indigo (`#111113`). `--radius-lg` 12→18px. - Cards: shadow depth replaces borders. Send buttons: indigo gradient + glow. ## 2026-03-10 — CI/CD infrastructure - `infra/Dockerfile.runner-base`: Ubuntu 24.04 + Python 3.12 + Node 22 LTS custom runner image. - All CI jobs use `py3.12-node22` runner; pip caching added; Node bumped to 22. ## 2026-03-06 — Session invalidation on credential change - `users.session_version` column; incremented on password change/reset; checked in `_check_auth`; mismatch evicts all other sessions. ## 2026-03-06 — DRY refactoring (backend + frontend) - `models/base.py`: `TimestampMixin` + `CreatedAtMixin` applied to all models (~60 lines removed). - `routes/utils.py`: `not_found()` + `parse_iso_date()` helpers. - Frontend composables: `useAutoSave`, `useEditorGuards`, `useTagSuggestions`, `useFloatingAssist`, `useListKeyboardNavigation`. - Shared CSS: `editor-shared.css`, `viewer-shared.css`. Net: −634 lines, +237 lines. ## 2026-03-06 — Project-aware writing assist, link suggestions, project-scoped RAG - Writing assist: `context_notes` param injects project notes; `definition`-tagged notes prioritised. - Link suggestions: detects note titles as plain text in body; one-click wikilink conversion. - Project-scoped RAG: `rag_project_id` param restricts semantic + keyword search to a project. - `search_notes` tool: upgraded to hybrid semantic + keyword; `relevance` field in results. - SSRF protection: `_is_private_url()` blocks private/loopback addresses; `follow_redirects=False`. - Config validation: `Config.validate()` at startup. ## 2026-03-05 — Note editor sidebar, full-document writing assist, drafts, version history - NoteEditorView: two-column layout; writing assistant always visible in sidebar. - `whole_doc` mode: assistant outputs complete revised document (not just selected section). - `DiffView.vue`: standalone diff component with equal-line collapsing (git-style context). - Persistent drafts: `note_drafts` table; draft auto-saved after assist generation; restored on load. - `note_versions` table: auto-snapshot on body change; version browser with diff + restore. ## 2026-03-05 — Task work log, task editor overhaul - `task_logs` table; `TaskLogSection.vue` with timestamps, duration badges, inline edit. - TaskEditorView: two-column layout with collapsible sidebar; sub-tasks section. - `log_work` LLM tool added to `_CORE_TOOLS`. ## 2026-03-04 — Per-conversation streaming, SSE reconnection, keyboard navigation - Per-conversation streaming state; auto-new chat on open. - SSE reconnection: chat `reconnectIfGenerating()` on mount + navigation; assist retry loop with `Last-Event-ID`. - Model load timeout raised 90s → 180s. - Writing assist retry loop (3 attempts, exponential backoff).