From 97d62a6a32a42b12c21dce7499848e830d50133b Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Thu, 12 Mar 2026 00:08:39 -0400 Subject: [PATCH 1/2] =?UTF-8?q?docs:=20update=20summary.md=20=E2=80=94=20D?= =?UTF-8?q?aily=20Briefing,=20CI=20release=20process,=20CalVer,=20dedup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- summary.md | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/summary.md b/summary.md index 57a01bb..394abc2 100644 --- a/summary.md +++ b/summary.md @@ -12,7 +12,52 @@ > Include file-level details in the commit body when the change is non-trivial. ## Last Updated -2026-03-11 — Multi-user sharing & collaboration, in-app notifications, groups management, backup rewrite. +2026-03-12 — Daily Briefing feature (full stack), CI release process fix, CalVer version tracking, content deduplication, task history improvements. + +**CI release process (2026-03-12):** +- `.forgejo/workflows/ci.yml`: removed `main` from `branches` push trigger. CI now fires only on `dev` pushes, `v*` tags, and pull requests. Main branch merges no longer trigger a run — the PR check covers pre-merge safety. Production release process: create a release via Forgejo UI on `main` with a `v*` tag → tag push event fires CI → build job pushes `:latest` + `:` Docker images to registry. Branch protection is not an obstacle because the UI release creates the tag through the API. + +--- + +**Daily Briefing — full feature (2026-03-11):** + +Backend: +- Migration `0026_add_briefing_tables.py`: `rss_feeds` (url, name, user_id), `rss_items` (feed_id FK, guid, title, url, summary, pub_date), `weather_cache` (user_id UNIQUE, lat, lon, location_name, forecast_json, fetched_at); added `conversation_type` TEXT and `briefing_date` DATE to `conversations`. +- `models/rss_feed.py`, `models/rss_item.py`, `models/weather_cache.py`: SQLAlchemy models with `to_dict()`. +- `services/weather.py`: geocoding via Nominatim (Open-Meteo), forecast fetch via Open-Meteo API, per-user DB cache with change detection. `get_weather(user_id)` returns structured dict with location, current, and 5-day forecast. +- `services/rss.py`: feedparser-based fetch, per-feed DB cache with prune-to-100. `get_rss_items(user_id, limit)` returns recent items across all user feeds. +- `services/briefing_profile.py`: service that reads user briefing config (CalDAV, weather, RSS, office days, tasks) and composes a profile note used as context for generation. +- `services/briefing_pipeline.py`: two-lane parallel gather (async `asyncio.gather` over weather, RSS, tasks, calendar) → LLM synthesis. Streams result into a `GenerationBuffer` using the same SSE infrastructure as chat. +- `routes/briefing.py`: RSS CRUD at `/api/briefing/feeds`, weather config at `/api/briefing/weather`, briefing config at `/api/briefing/config`, `POST /api/briefing/trigger` to manually fire a slot. Registered as `briefing_bp` in `app.py`. +- `routes/chat.py`: `GET /api/briefing/conversations/today` — creates the day's briefing conversation if absent (type=`briefing`, `briefing_date=today`); `GET /api/briefing/conversations` — lists all past briefing conversations. +- `services/generation_task.py` / `routes/chat.py`: `conversation_type` filter support so briefing conversations are excluded from the main chat list and vice versa. +- APScheduler integration: `services/scheduler.py` runs briefing slots (morning, midday, evening) on schedule with catch-up logic — if a slot was missed while the server was down, it runs immediately on next startup. +- LLM tools: `get_weather` and `get_rss_items` added to `_CORE_TOOLS` in `services/tools.py`. + +Frontend: +- `frontend/src/views/BriefingView.vue` (new): primary briefing page. Soft chat-style layout. Top section shows the digest card (expandable) followed by the conversation thread. Reply bar at bottom — sends via standard chat endpoints and streams via SSE. Manual "Refresh" button triggers a new briefing generation. Conversation history dropdown (past briefing dates) in header. +- `frontend/src/components/BriefingSetupWizard.vue` (new): 4-step setup wizard shown on first visit when briefing is not configured (welcome → location → office days → RSS feeds). Completes by enabling briefing in settings. +- `frontend/src/views/SettingsView.vue`: Briefing tab added (`briefing` in `VALID_TABS`): enable toggle, location geocoding input (live preview), office days checkboxes, time slot toggles (morning/midday/evening), RSS feed CRUD (add URL + name, list, delete), push notification toggle for briefing completion. +- `frontend/src/router/index.ts`: `/briefing` route added → `BriefingView`. +- `frontend/src/components/AppHeader.vue`: "Briefing" nav link added. + +--- + +**Previous session (2026-03-11):** + +**Version footer + CalVer tracking:** +- `4636c9a` Add CalVer build-time version tracking: `Dockerfile` injects `BUILD_VERSION` ARG as `VITE_APP_VERSION` env var during frontend build. Frontend reads `import.meta.env.VITE_APP_VERSION` and displays it in the Settings footer. Docker image tags include `:` on release builds. +- `2cb4e6d` Version footer, task history UI, note version retention policy: Settings page shows `v` in footer. Task history now displays inline in a sidebar within the task editor (previously required navigation). Note version retention policy configurable (max versions per note, default 20 — enforced at write time in `services/note_versions.py`). + +**Content deduplication + history sidebar:** +- `6d593a0` Move history to sidebar, simplify task assist, add content deduplication: `HistoryPanel` moved to a collapsible right sidebar in `NoteEditorView` (previously a modal overlay). Writing assist panel simplified — removed multi-step UX, direct instruction input. `build_context()` in `services/llm.py` deduplicates injected content (RAG-found notes, sidebar includes, context notes) by ID so the same note cannot appear in multiple context blocks. + +**ShareDialog CSS fix:** +- `a63f067` Fix ShareDialog transparent background — replaced undefined CSS custom property references with explicit values. + +--- + +**Previous session (2026-03-11 — Multi-user & backup):** Multi-user sharing & collaboration, in-app notifications, groups management, backup rewrite. **Multi-user sharing & collaboration:** - `alembic/versions/0025_add_sharing_and_notifications.py` (new): creates `groups`, `group_memberships`, `project_shares`, `note_shares`, `notifications` tables. Partial unique indexes via raw SQL. CHECK constraint enforces exclusive user/group target on share rows. From d2605287f7f000becbe4e0386e048bef8d578f65 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Thu, 12 Mar 2026 06:43:46 -0400 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20briefing=20pipeline=20=E2=80=94=20pa?= =?UTF-8?q?ss=20user=5Fid=20to=20is=5Fcaldav=5Fconfigured,=20use=20p.title?= =?UTF-8?q?=20not=20p.get()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/fabledassistant/services/briefing_pipeline.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fabledassistant/services/briefing_pipeline.py b/src/fabledassistant/services/briefing_pipeline.py index 5b56d76..62b48e8 100644 --- a/src/fabledassistant/services/briefing_pipeline.py +++ b/src/fabledassistant/services/briefing_pipeline.py @@ -80,7 +80,7 @@ async def _gather_internal(user_id: int) -> dict: # Calendar events today calendar_events = [] try: - if is_caldav_configured(): + if is_caldav_configured(user_id): events = await list_events(user_id, start=today, end=today) calendar_events = [ f"{e.get('summary', 'Event')} at {e.get('dtstart', 'unknown time')}" @@ -94,7 +94,7 @@ async def _gather_internal(user_id: int) -> dict: try: projects = await list_projects(user_id) for p in projects[:5]: - projects_summary.append(p.get("title", "Untitled project")) + projects_summary.append(p.title or "Untitled project") except Exception: logger.warning("Failed to gather projects for briefing", exc_info=True)