Commit Graph
100 Commits
Author SHA1 Message Date
bvandeusenandClaude Sonnet 4.6 e2133529a0 docs: add missing content from summary.md (API reference, Android app, file structure, LLM internals)
- docs/api-reference.md: complete REST API endpoint reference (~60+ routes)
- docs/android-app.md: Flutter companion app stack, architecture, feature status
- docs/architecture.md: detailed file-by-file reference for all backend services and frontend components
- docs/features.md: LLM pipeline internals (intent routing, tool loop, duplicate guards, image search, research pipeline), roadmap
- docs/development.md: full migration chain (0001–0026) with naming and caveats
- README.md: link to new api-reference and android-app docs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 21:25:12 -04:00
bvandeusenandClaude Sonnet 4.6 47e248d9ac docs: restructure documentation into docs/ directory, slim README, add project .mcp.json
- README.md: reduced to overview + quick start + links to docs/
- docs/architecture.md: stack, design decisions, data models, key services
- docs/configuration.md: all env vars, docker-compose setup, production + security
- docs/development.md: dev workflow, CI/CD, migrations, release process
- docs/features.md: detailed feature breakdown + keyboard shortcuts
- docs/api-keys-and-mcp.md: API key management + Fable MCP install guide
- docs/sso-oauth.md: OAuth/OIDC setup (replaces docs/oauth-setup.md)
- docs/changelog.md: development history from summary.md
- Remove summary.md (content distributed across docs/)
- Remove docs/oauth-setup.md (superseded by docs/sso-oauth.md)
- .gitignore: add .mcp.json

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 21:03:15 -04:00
bvandeusenandClaude Sonnet 4.6 553c38200a feat(fable-mcp): build wheel in Docker image, serve download, add admin log tool, and Settings install UI
- Dockerfile: build fable-mcp wheel into /app/dist/ during image build
- routes/fable_mcp_dist.py: GET /api/fable-mcp/info + /download endpoints
- app.py: register fable_mcp_dist_bp
- fable_mcp/tools/admin.py: get_app_logs() hitting /api/admin/logs
- fable_mcp/server.py: fable_get_app_logs MCP tool
- SettingsView: "Fable MCP" section in API Keys tab with download button and install instructions
- client.ts: getFableMcpInfo() helper
- ci.yml: add fable-mcp/** to trigger paths

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 17:26:05 -04:00
bvandeusenandClaude Sonnet 4.6 6564a03c0e feat(settings): model management UI — pull, delete, VRAM status
Backend:
- Enrich GET /api/chat/models to also hit /api/ps and return loaded:bool
  and modified_at alongside name/size, using parallel gather

Frontend (Settings → General):
- Model list: each row shows name (monospace), size (GB/MB), 'in VRAM' badge
  if currently loaded, 'default' badge if it's the configured default
- Delete button per row; disabled while deletion in progress
- Pull form: text input (Enter submits) + Pull button
- Suggestion chips for qwen3:7b/14b/4b, llama3.1:8b, nomic-embed-text;
  disabled if already installed
- Progress display during pull: status text + determinate bar when
  Ollama reports total/completed, indeterminate animation otherwise
- Refresh button reloads the list; list auto-refreshes after pull/delete
- Link to ollama.com/library for model discovery

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 02:52:31 -04:00
bvandeusenandClaude Sonnet 4.6 9cd0de3883 fix(briefing): deadlock in scheduler startup
start_briefing_scheduler was called from before_serving (event loop thread)
and used run_coroutine_threadsafe(...).result() which blocks the calling
thread waiting for the coroutine to complete — but since the calling thread
IS the event loop, the coroutine could never run, causing a 10s timeout and
zero jobs scheduled.

Fix: make start_briefing_scheduler async and await _get_briefing_enabled_users()
directly. Also use asyncio.create_task for the catch-up rather than
run_coroutine_threadsafe. The background thread jobs (_run_user_slot_sync)
continue to use run_coroutine_threadsafe correctly since they run on the
APScheduler thread, not the event loop thread.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 02:18:30 -04:00
bvandeusenandClaude Sonnet 4.6 0db5dd126c fix(task-viewer): esc capture phase to prevent App.vue handler conflict
Register the Esc keydown listener in capture phase (useCapture=true) and
call stopPropagation() so App.vue's document-level handler never fires.
Without this, both handlers ran: App.vue pushed "/" and the component
pushed "/projects/:id", with non-deterministic winner. Also fixes the
blur-then-navigate issue where App.vue blurring an input caused the
component's handler to see body as the active element and navigate
immediately instead of stopping at the blur step.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 01:05:12 -04:00
bvandeusenandClaude Sonnet 4.6 a2ba90160c feat: kanban status buttons, task back-nav, RSS UI, weather search, briefing fixes
Project view:
- Add inline status advance buttons on kanban task cards (todo→in_progress,
  in_progress→done); buttons reveal on hover, stop link navigation

Task viewer:
- Back button navigates to task's project instead of /tasks when project_id set
- Esc key navigates to project (or /tasks); blurs focused element first

Quick capture:
- Use user's configured model instead of hardcoded Config.OLLAMA_MODEL
- Remove create_project from classifier prompt (tool not offered, caused
  task-shaped inputs to silently fall through to note fallback)

Briefing scheduler:
- Fix get_event_loop() → get_running_loop() so background thread uses the
  correct hypercorn event loop (jobs were scheduling but never executing)
- Suppress bare greeting when both LLM synthesis lanes return empty

RSS feed UI (SettingsView):
- Show last-fetched age, category badge, and feed URL per row
- Category input field when adding a feed
- Refresh all button: fetches latest items, reloads list, toasts with count
- Enter key submits add-feed form; better empty-state hint with example feeds

Weather tool:
- Accept any city/region name in addition to 'home'/'work'/'all'
- Geocodes via Nominatim + fetches live from Open-Meteo for arbitrary queries

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 00:42:01 -04:00
bvandeusenandClaude Sonnet 4.6 a9414cf949 chore: merge main into dev, resolve tools.py conflict
Accepted main's semantic duplicate threshold (>= 80 chars) over dev's
(>= 200 chars) for both note and task body checks.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 23:16:09 -04:00
bvandeusenandClaude Sonnet 4.6 00dee5ea0e ci: remove redundant pull_request trigger
All code goes through dev first where CI runs on push. PR runs
(dev→main) were duplicating already-validated checks and causing
queue contention. Build job was already gated to push-only events.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 22:53:32 -04:00
bvandeusenandClaude Sonnet 4.6 55e260b392 fix(settings): fallback clipboard copy for http dev environments
navigator.clipboard.writeText() requires a secure context (HTTPS) and
silently fails on http://. Add an execCommand fallback so the API key
copy button works on non-secure dev instances.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 22:39:55 -04:00
bvandeusenandClaude Sonnet 4.6 359cb24d9a feat(fable-mcp): implement FableClient, all tool modules, and server.py
- client.py: async httpx wrapper, FableAPIError, stream_get() SSE generator,
  singleton init_client()/get_client() and _reset_client() for tests
- tools/: notes, tasks, projects, milestones, search, chat — thin async
  functions that accept FableClient and call Fable REST endpoints
- server.py: FastMCP entry point with 20 tools registered via @mcp.tool(),
  each opening a fresh FableClient context per call; validates env vars at startup
- tests: 34 tests covering client HTTP behaviour, error handling, singleton,
  SSE streaming, and all tool modules

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 21:51:32 -04:00
bvandeusenandClaude Sonnet 4.6 6180ee65c3 feat: add .env and Claude config downloads to API key reveal
After creating a key, two download buttons appear:
- 'Download .env' — pre-filled FABLE_URL + FABLE_API_KEY
- 'Download Claude config' — ready-to-paste mcpServers JSON block

Also adds Future Task A (in-app install instructions) and Future Task B
(Forgejo MCP) to the implementation plan.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 21:44:34 -04:00
bvandeusenandClaude Sonnet 4.6 66571e16c1 feat: scaffold fable-mcp Python package
pyproject.toml with mcp[cli]/httpx/python-dotenv deps, entry point
fable-mcp=fable_mcp.server:main, dev deps for testing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 21:21:55 -04:00
bvandeusenandClaude Sonnet 4.6 73eb34d722 feat: add API Keys tab to Settings UI
Create/revoke API keys with read/write scope selector. One-time key
reveal with copy button. Keys table showing prefix, scope badge, last
used. Inline revoke confirmation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 21:15:24 -04:00
bvandeusenandClaude Sonnet 4.6 47b4af281b feat: API key routes, search endpoint, conversation type wiring
- GET/POST/DELETE /api/api-keys blueprint registered
- GET /api/search?q=&content_type=&limit= semantic search endpoint
- create_conversation gains conversation_type param (default "chat")
- cleanup_old_conversations excludes mcp type from retention sweep
- POST /api/chat/conversations accepts conversation_type body field

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 21:01:04 -04:00
bvandeusenandClaude Sonnet 4.6 e8a4ca915a feat: add bearer token auth to _check_auth, falls back to session
Checks Authorization: Bearer header first, hashes token, looks up in
api_keys. Read-only keys get 403 on non-GET. Admin routes inaccessible
to non-admin key owners. Session path unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 20:57:26 -04:00
bvandeusenandClaude Sonnet 4.6 583a140485 feat: add ApiKey service with create/list/revoke/lookup
SHA-256 hashed keys, fmcp_ prefix, soft-delete revocation. Tests cover
pure logic (hash, prefix, format, uniqueness, scope validation) and
auth middleware bearer token path (pending auth.py update).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 20:56:51 -04:00
bvandeusenandClaude Sonnet 4.6 7358909432 feat: add ApiKey model and migration 0027
Adds api_keys table with user_id FK, key_hash, key_prefix, scope
(read/write), last_used_at, revoked_at. SHA-256 hashed, soft-delete.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 20:52:25 -04:00
bvandeusenandClaude Sonnet 4.6 c24b21f259 docs: add Fable MCP implementation plan
17-task TDD plan covering Phase 1 (API key auth, search endpoint,
conversation type wiring, Settings UI) and Phase 2 (fable-mcp Python
package with all tool modules, tests, and Claude Code registration).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 20:24:51 -04:00
bvandeusenandClaude Sonnet 4.6 e54d172487 docs: fix 8 spec review issues in Fable MCP design
- Add migration down_revision = "0026"
- Document create_conversation service + route changes for conversation_type
- Document SSE wire format (endpoints, event schema, termination event)
- Add retention sweep exclusion for mcp conversation type
- Rename type→content_type in search, document is_task mapping
- Clarify API keys cannot access admin-protected routes
- Document POST /api/chat/conversations accepting conversation_type body field
- Add delete_milestone to milestones tool list

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 20:03:53 -04:00
bvandeusenandClaude Sonnet 4.6 f9973e22f1 docs: add Fable MCP design spec
Design for two sub-projects: API key auth support in fabledassistant
(bearer tokens, read/write scope, Settings UI) and a standalone Python
MCP server at fable-mcp/ exposing notes, tasks, projects, milestones,
semantic search, and chat tools to Claude Code.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 20:00:26 -04:00
bvandeusenandClaude Sonnet 4.6 33f52081e5 docs: update summary.md for 2026-03-16 session
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 08:29:04 -04:00
bvandeusenandClaude Sonnet 4.6 a58dbe79f2 fix: fuzzy project resolution and guard against accidental project creation
- _resolve_project now uses reverse-substring + SequenceMatcher (≥0.55)
  so partial names like "Famous Supply" match "Famous Supply Co." reliably
- create_project runs similar-project checks before the confirmed gate so
  confirmed=true can't bypass them; threshold lowered to 0.55 to catch
  abbreviated names
- Error messages on project-not-found no longer suggest create_project,
  directing the model to list_projects first
- Tool description updated to explicitly prohibit calling create_project
  in response to a lookup failure
- tasks.py: fire-and-forget embedding update on create/update
- briefing_pipeline.py: await is_caldav_configured (was sync call)
- notes.py: remove erroneous duplicate count_query filter

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 07:44:27 -04:00
bvandeusenandClaude Sonnet 4.6 0d41b8be34 Fix briefing settings not loading when tab is active on mount
The watch(activeTab) only fires on changes, not the initial value.
When localStorage had settings_tab="briefing", onMounted skipped
loadBriefingTab() so the form always showed default empty values.
Follows the same pattern already used for the groups panel (line 338).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 23:05:10 -04:00
bvandeusenandClaude Sonnet 4.6 9cb3700a5c fix: add temp_unit and timezone to inline BriefingConfig literals
BriefingSetupWizard and SettingsView had hardcoded initial objects that
predated the new fields, causing TS2345 type errors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 18:33:17 -04:00
bvandeusenandClaude Sonnet 4.6 b44d8496bc fix: queue drain survives navigation away from a streaming conversation
Previously the queue drain guard (currentConversation.id === convId) meant
that if the user navigated to ChatView while a workspace stream was running,
the workspace's queued messages were silently abandoned — they sat in
localStorage indefinitely with no code path to resume them.

- Extract _tryDrainQueue(convId) with the same guard logic
- Call it at stream-end (replaces the inline block)
- Call it in fetchConversation after _loadQueue, so returning to a
  conversation with orphaned queue messages drains them automatically
- Order is now preserved: messages drain in the order they were queued,
  even across navigation events

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 18:08:45 -04:00
bvandeusenandClaude Sonnet 4.6 3eb61950c9 fix: auto-scroll workspace chat when messages are added
Watch currentConversation.messages.length so the chat panel scrolls to
the bottom when user messages are appended or assistant messages land,
not only while streaming content is updating.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 18:02:28 -04:00
bvandeusenandClaude Sonnet 4.6 beb57876fb feat: update_milestone tool and workspace milestone refresh
- Add update_milestone LLM tool: accepts project + milestone title to
  look up, then applies any of title/description/status changes
- WorkspaceView SSE watcher now triggers taskPanelRef.reload() on
  create_milestone and update_milestone success, so milestone groups
  appear immediately without a manual refresh

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 17:58:13 -04:00
bvandeusenandClaude Sonnet 4.6 5ea3bb5aff feat: per-user timezone-aware briefing scheduler
Briefing slots (4am/8am/12pm/4pm) now fire in each user's local timezone
rather than UTC, so the schedule matches the user's actual day.

Backend:
- briefing_scheduler: replaced 4 global UTC cron jobs with per-user
  CronTrigger jobs keyed to the user's IANA timezone; update_user_schedule()
  live-patches the scheduler when config is saved (no restart needed)
- Catchup logic evaluates missed slots in the user's local timezone
- put_config route calls update_user_schedule() after saving

Frontend:
- New Timezone section in Briefing settings: text input pre-filled from
  browser (Intl.DateTimeFormat) with a Detect button for re-detection
- Slot times shown as fixed local times (4:00 am etc.) with the configured
  timezone displayed beneath, replacing the old UTC-conversion display
- timezone field added to BriefingConfig type and default

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 08:34:31 -04:00
bvandeusenandClaude Sonnet 4.6 6e57ce4555 feat: temperature unit preference and slot timezone display for briefing
- Add °C/°F toggle in briefing settings; persisted in briefing_config.temp_unit
- briefing_pipeline reads temp_unit and converts Open-Meteo Celsius values
  before passing them to the LLM (both full compilation and slot injection)
- Scheduled Slots section now shows each UTC slot time converted to the
  user's browser local time, plus a line confirming which timezone the
  browser is using

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 08:29:40 -04:00
bvandeusen d2605287f7 fix: briefing pipeline — pass user_id to is_caldav_configured, use p.title not p.get() 2026-03-12 06:43:46 -04:00
bvandeusen 97d62a6a32 docs: update summary.md — Daily Briefing, CI release process, CalVer, dedup 2026-03-12 00:08:39 -04:00
bvandeusen 247a9ab9f9 ci: don't run on main branch push — PR trigger covers pre-merge safety
Release is now cut by creating a Forgejo release on main with a v* tag;
the tag push event triggers CI + Docker build/push.
2026-03-12 00:03:50 -04:00
bvandeusen 241e14bd8c updating readme to reflect significant changes 2026-03-11 22:56:02 -04:00
bvandeusenandClaude Sonnet 4.6 1686289af4 feat: daily briefing frontend — view, setup wizard, settings tab, nav
- BriefingView: soft chat UI with conversation history dropdown, SSE
  streaming via chat store, manual refresh trigger, today/past conv toggle
- BriefingSetupWizard: 4-step first-time setup overlay (welcome →
  locations → office days → RSS feeds)
- SettingsView: Briefing tab with enable toggle, location geocoding,
  office day picker, slot toggles, RSS feed management, notifications
- AppHeader + router: /briefing route and nav link (desktop + mobile)
- client.ts: briefing types and API functions (config, feeds, convs,
  geocode, trigger)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 20:30:45 -04:00
bvandeusenandClaude Sonnet 4.6 d131621de6 feat: APScheduler briefing slots, catch-up logic, profile close-out
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 20:07:16 -04:00
bvandeusenandClaude Sonnet 4.6 b66e6e0ad5 feat: briefing conversation management API
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 20:07:13 -04:00
bvandeusenandClaude Sonnet 4.6 ebbf1a91f2 feat: briefing pipeline — parallel gather, two-lane LLM synthesis
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 20:07:10 -04:00
bvandeusenandClaude Sonnet 4.6 bc2119c067 feat: briefing profile note service
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 20:07:05 -04:00
bvandeusenandClaude Sonnet 4.6 1f9ffe74bc feat: add APScheduler dependency for briefing scheduler
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 20:07:02 -04:00
bvandeusenandClaude Sonnet 4.6 42b0d8c4ac feat: filter chat conversations by type; add get_weather and get_rss_items LLM tools
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 19:46:08 -04:00
bvandeusenandClaude Sonnet 4.6 ae3dff0952 feat: briefing API routes — feeds CRUD, weather, config
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 19:46:04 -04:00
bvandeusenandClaude Sonnet 4.6 9bb054f3d5 feat: RSS service — feedparser fetch, cache, prune
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 19:46:01 -04:00
bvandeusenandClaude Sonnet 4.6 bbdcea57a7 feat: weather service — geocoding, Open-Meteo forecast, cache, change detection
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 19:45:59 -04:00
bvandeusenandClaude Sonnet 4.6 dd8dc519ab feat: migration 0026 — rss_feeds, rss_items, weather_cache, conversation briefing columns
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 19:45:55 -04:00
bvandeusenandClaude Sonnet 4.6 9d31c1bab7 feat: add RssFeed, RssItem, WeatherCache models
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 19:45:50 -04:00
bvandeusenandClaude Sonnet 4.6 fa81509c9f feat: add conversation_type and briefing_date to Conversation model
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 19:45:47 -04:00
bvandeusenandClaude Sonnet 4.6 598901b9a2 feat: add feedparser dependency for RSS service
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 19:45:43 -04:00
bvandeusenandClaude Sonnet 4.6 a63f06712f Fix ShareDialog transparent background — replace undefined CSS vars
--color-surface → --color-bg-card
--color-muted   → --color-text-muted
--color-hover   → --color-bg-secondary

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 17:38:51 -04:00
bvandeusenandClaude Sonnet 4.6 6d593a0ef6 Move history to sidebar, simplify task assist, add content deduplication
- VersionHistorySection.vue: new sidebar component — collapsible timestamp list,
  click any snapshot to see inline diff + Restore/Back; replaces the modal
- NoteEditorView: remove History toolbar button + HistoryPanel modal, add
  VersionHistorySection at bottom of sidebar
- TaskEditorView: remove floating overlay assist panel + toggle button; add
  Writing Assistant section in sidebar matching note editor (scope selector,
  instruction textarea, generate/proofread/accept/reject); main area now shows
  streaming preview and DiffView like note editor; add VersionHistorySection
- note_versions.py: add content deduplication before time-gap check — identical
  body + title + tags skips snapshot regardless of interval (git semantics)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 16:56:30 -04:00
bvandeusenandClaude Sonnet 4.6 2cb4e6d3b2 Add version footer, task history UI, and note version retention policy
- App.vue: quiet version footer (fetches /api/version, 0.45 opacity)
- TaskEditorView: History button + HistoryPanel integration (mirrors NoteEditorView)
- note_versions.py: raise MAX_VERSIONS 20→50; add 5-min minimum interval gate
  to prevent autosave exhausting history slots

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 16:23:56 -04:00
bvandeusenandClaude Sonnet 4.6 9383f10dab ci: build only on dev push and release tag, not on main merge
Merging to main now runs CI checks only. The release tag (v*) is the
sole trigger for a production :latest image, eliminating the redundant
double-build that previously occurred on every PR merge + tag pair.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 16:13:18 -04:00
bvandeusenandClaude Sonnet 4.6 4636c9a973 Add CalVer build-time version tracking
- Dockerfile: ARG BUILD_VERSION=dev → ENV APP_VERSION baked into image
- ci.yml: BUILD_VERSION passed as build-arg; set to git tag name on v*
  tag builds, "dev" on branch builds
- routes/api.py: GET /api/version returns {"version": APP_VERSION}
- SettingsView: fetches /api/version on mount, displays in About section
  under General tab

Version source of truth is the git tag (YY.MM.DD.N CalVer).
pyproject.toml / package.json versions are no longer maintained.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 16:10:57 -04:00
bvandeusenandClaude Sonnet 4.6 767fa73e1d Update summary.md and README for sharing/collaboration/backup session
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 15:40:25 -04:00
bvandeusenandClaude Sonnet 4.6 c7ef709633 Multi-user sharing, groups, and in-app notifications
Backend:
- Alembic migration 0025: groups, group_memberships, project_shares,
  note_shares, notifications tables
- models: Group, GroupMembership, ProjectShare, NoteShare, Notification
- services/access.py: permission resolution (viewer/editor/admin/owner)
- services/groups.py + routes/groups.py: full group CRUD + membership
- services/sharing.py + routes/shares.py: project/note sharing API
- services/notifications.py: in-app notification create/list/mark-read
- routes/in_app_notifications.py: GET/POST notification endpoints
- routes/users.py: user search endpoint
- services/projects.py + services/notes.py: *_for_user variants
- routes updated to use *_for_user on get/list; adds permission field
- app.py: register all new blueprints

Frontend:
- api/client.ts: ShareEntry, GroupEntry, NotificationEntry types + helpers
- stores/notifications.ts: Pinia store with polling
- NotificationBell.vue: bell icon + badge + dropdown toggle + 60s poll
- NotificationsPanel.vue: unread notification list with mark-all-read
- ShareDialog.vue: teleport modal for sharing projects/notes with users/groups
- SharedWithMeView.vue: /shared route listing shared projects and notes
- AppHeader: NotificationBell, Shared nav link
- ProjectView, NoteViewerView, TaskViewerView: Share button + ShareDialog
- SettingsView: Groups admin tab with create/delete groups + member mgmt
- router: /shared route

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 15:37:00 -04:00
bvandeusenandClaude Sonnet 4.6 878b149f4d feat(backup): v2 backup/restore with all models and FK re-mapping
Export now includes: projects, milestones, task_logs, note_drafts,
note_versions, push_subscriptions; full user fields (oauth_sub,
session_version); full message fields (status, tool_calls).

Restore v2 builds ID maps for all tables and patches FKs in dependency
order (users → projects → milestones → notes → child records). Notes
restored in two passes to handle self-referential parent_id correctly.
v1 backups still restore via _restore_v1 path (parent_id bug also fixed).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 11:57:21 -04:00
bvandeusenandClaude Sonnet 4.6 de4ab53363 Add implementation plans: sharing/collaboration and backup rewrite
- docs/plans/2026-03-11-sharing-collaboration.md
  7 chunks / 12 tasks covering DB migration, models, access control service,
  groups CRUD, sharing service, notifications, and all frontend components

- docs/plans/2026-03-11-backup-rewrite.md
  4 tasks covering v2 export (full + user), v2 restore with FK re-mapping,
  and round-trip verification; v1 restore preserved

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 11:46:54 -04:00
bvandeusenandClaude Sonnet 4.6 5d3f8683a4 ProjectView UI overhaul: Fraunces title, gradient CTA, stat chips, kanban colors
- Gradient 'Open Workspace' CTA replacing flat button
- Fraunces serif project title input (1.75rem, borderless underline)
- Summary stat chips with colored status dots (todo/in-progress/done/notes)
- Kanban columns with 3px color-coded top border (muted/blue/green)
- Task cards with left-border priority indicator (high=red, med=amber, low=blue)
- Activity timestamp with clock icon via relativeTime composable
- Shimmer skeleton loader during project fetch
- Quieter danger-outline delete button
- Milestone chevron/edit/delete use SVG icons
- Notes tab uses relative timestamps + document icon
- Remove unused priorityClass/formatDate functions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 11:11:41 -04:00
bvandeusenandClaude Sonnet 4.6 8a83ba8e11 Task views: same UI refinement pass as note views
TaskEditorView:
- Back button standardized to "← Tasks"
- Toolbar now on same row as Write/Preview tabs (flex-direction row)
- Save button and title input inherit gradient/Fraunces from editor-shared.css

TaskViewerView:
- Shimmer skeleton loader replaces plain "Loading..." text
- Task title uses Fraunces font at 2rem
- Edit button becomes primary gradient CTA
- Meta timestamps show clock/pencil SVG icons
- Backlinks section: card grid with colored type badges (note=indigo, task=amber)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 11:00:08 -04:00
bvandeusenandClaude Sonnet 4.6 978a2627fb UI refinement: toolbar icons, gradient CTA, Fraunces title, viewer polish
MarkdownToolbar:
- Replace text labels with SVG icons (Material Design paths)
- Group buttons with visual separators: text / headings / lists / code / links
- Add Strikethrough and Blockquote buttons (previously missing)
- Active state: tinted bg + ring instead of solid fill; hover lift shadow
- Toolbar container: pill shape with border (matches rest of UI)

editor-shared.css:
- btn-save: gradient + glow (linear-gradient(135deg, #6366f1, #4f46e5))
- title-input: borderless with Fraunces font and focus underline

NoteEditorView:
- Back button label standardized to "← Notes" (was "Back")
- Write/Preview tabs and toolbar now share the same row (flex-direction row)

NoteViewerView:
- Shimmer skeleton loader replaces plain "Loading..." text
- Note title uses Fraunces font at 2rem
- Edit button becomes primary gradient CTA
- Meta line shows clock/pencil SVG icons alongside timestamps
- Backlinks section: card grid with colored type badges (note=indigo, task=amber)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 10:57:02 -04:00
bvandeusenandClaude Sonnet 4.6 810f63e749 Associate research_topic notes with workspace project
run_research_pipeline now accepts project_id; generation_task.py passes
workspace_project_id when the tool is called from a workspace context.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 09:29:56 -04:00
bvandeusenandClaude Sonnet 4.6 71301f631d Workspace UI overhaul: rail note panel, split task detail, project goal header
- WorkspaceNoteEditor: replace toggling list/editor views with always-visible
  left rail (155px) showing note list alongside editor pane; persist last-open
  note per project in localStorage (workspace_note_{projectId})
- WorkspaceTaskPanel: add priority dot + due date on task rows; replace full
  overlay detail with bottom-split (44% list / 56% detail); close button
  replaces back nav; active row highlighted; fade transition
- WorkspaceView: show project goal in header instead of 'Workspace'; non-equal
  panel widths (0.8fr / 1.1fr / 1.1fr); empty chat quick-action chips (Project
  status / New note / Add tasks); prefill() helper

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 23:51:17 -04:00
bvandeusenandClaude Sonnet 4.6 e4b2cd3aeb Tasks list: smart sections view (overdue / today / this week / upcoming / no date / done)
Replaces the flat sorted list with date-aware sections that surface what
needs attention first. Completed tasks go into a collapsed section at the
bottom. Grouped-by-project mode is preserved as the second toggle option.
TaskCard compact restored to card hover/lift style.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 23:12:07 -04:00
bvandeusenandClaude Sonnet 4.6 3b5e84052a Remove docs/superpowers and gitignore it
Planning artifacts (specs/plans) generated by Claude Code sessions don't belong
in the repo. Removing existing files and ignoring the directory going forward.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 22:40:44 -04:00
bvandeusenandClaude Sonnet 4.6 6e9ec26c8e UI polish: notes/tasks list views
- NotesListView: skeleton shimmer for grid and list modes; grid columns change from fixed 3 to auto-fill(260px)
- NoteCard compact: flat border-bottom row style instead of stacked mini-cards; gradient fade mask on grid preview text
- TaskCard compact: flat border-bottom row style; show up to 2 tags
- TasksListView: gradient + shadow on New Task button (matches NotesListView)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 22:39:05 -04:00
bvandeusenandClaude Sonnet 4.6 f755067cbd feat: inline Logs panel into Settings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 22:26:52 -04:00
bvandeusenandClaude Sonnet 4.6 c2565fb818 feat: inline Users panel into Settings
Ports UserManagementView logic (users list, invitations, registration
toggle) directly into SettingsView as a lazy-loaded 'users' admin tab.
Adds loadLogsPanel stub for Task 4. Adds apiDelete import and User type
import at script top.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 22:22:18 -04:00
bvandeusenandClaude Sonnet 4.6 7c6d15179d feat: settings sidebar layout + card header style
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 21:54:00 -04:00
bvandeusenandClaude Sonnet 4.6 befee1110b feat: replace gear dropdown with direct settings link
Remove the gear dropdown menu from AppHeader; cog icon is now a plain
router-link to /settings. Admin-only /admin/users and /admin/logs routes
are replaced with redirects to /settings. Mobile menu admin links removed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 21:13:43 -04:00
bvandeusenandClaude Sonnet 4.6 89f3d94895 docs: settings rework implementation plan
Tasks: AppHeader/router cleanup, SettingsView sidebar layout,
inline Users and Logs panels.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 21:11:12 -04:00
bvandeusenandClaude Sonnet 4.6 1047298765 docs: settings rework design spec
Sidebar navigation with User/Admin groups, Option-A grouped card
content style, cog header direct link, Users and Logs panels inlined.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 20:43:04 -04:00
bvandeusenandClaude Sonnet 4.6 a6ce287be3 Dashboard: Mission Control layout — hero project, project grid, inbox
- Hero card: last-active project (by most recent note/task updated_at),
  prominent 'Open Workspace' button, milestone bars, 5 recent items,
  highest-priority next-up todo task
- Projects grid: all other active projects with small workspace button,
  urgency badges (in progress / todo count), milestone bars, hover lift
- Inbox section: collapsible strip of orphaned tasks + notes (no project)
- Skeleton shimmer replaces plain 'Loading...' text
- Chat widget + quick actions unchanged at top

Backend additions used: ?no_project=true on both /api/notes and
/api/tasks; ?project_id= on /api/tasks (committed separately)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 20:16:55 -04:00
bvandeusenandClaude Sonnet 4.6 1c6d009603 API: no_project filter + project_id on tasks route
- list_notes(): add no_project param → Note.project_id.is_(None) filter
- GET /api/notes: expose ?no_project=true
- GET /api/tasks: add ?project_id= and ?no_project=true pass-through

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 20:04:03 -04:00
bvandeusenandClaude Sonnet 4.6 404aba1461 Task UX: forward-only advance button, auto edit mode, reload on save
TaskViewerView:
- Add '→ In Progress' / '→ Done' advance button in toolbar (forward-only,
  hidden when status is already done)
- Styled with primary indigo gradient to distinguish from secondary actions

TaskEditorView:
- showPreview now defaults false; set to true after load only when body
  has content — empty tasks open directly in Write mode
- Save on an existing task now calls window.location.reload() so the
  preview/edit mode re-evaluates from the freshly persisted body

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 18:50:36 -04:00
bvandeusenandClaude Sonnet 4.6 bb1f29c8e2 Header: increase height and logo size for better visibility
- Nav padding: 0.5rem → 0.75rem vertical, 1.25rem → 1.5rem horizontal
- AppLogo size: 24px → 34px so the book detail is legible

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 18:36:10 -04:00
bvandeusenandClaude Sonnet 4.6 474ed1fe05 UI polish: hover lift, skeleton loaders, empty states, badge contrast, transitions
Card & row interactions:
- NoteCard, TaskCard, ProjectCard: translateY(-2px) lift on hover + extended transitions
- Kanban task cards (ProjectView), note rows: same lift treatment
- Task rows in flat/grouped list: soft indigo bg tint on hover
- Chat conversation items: left-border accent + bg tint on hover (was border-only)
- HomeView project mini-cards: translateY(-2px) lift on hover

Global feedback:
- Buttons: scale(0.97) on :active press (theme.css, :not(:disabled) scoped)
- TagPill: color → primary + indigo bg tint on hover
- StatusBadge: increased opacity 15% → 22% + stronger text color for readability

Loading & empty states:
- TasksListView: shimmer skeleton loader (6 rows) + rich empty state with CTA
- ProjectListView: shimmer skeleton cards (4) + rich empty state with CTA
- HomeView: section-empty messages for overdue/high-priority/notes sections
- ProjectView kanban columns: dashed-border styled empty state

Transitions & graph:
- WorkspaceView panels: panel-fade opacity transition on show/hide (v-if inner wrap)
- GraphView D3 nodes: grow to r*1.3 + bold label on mouseover (150ms transition)
- Milestone action buttons: base opacity 0 → 0.35 for discoverability

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 18:34:52 -04:00
bvandeusenandClaude Sonnet 4.6 3ef0e9f2c5 Delete empty untitled chat on navigate-away from ChatView
Previously the empty-conversation cleanup only fired when switching
between chats (convId watcher). Navigating to /notes, /tasks, etc.
left orphaned untitled empty conversations. onUnmounted now runs the
same check, deleting the current conversation if message_count === 0.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 18:02:57 -04:00
bvandeusenandClaude Sonnet 4.6 16220c57a0 Fix user bubble text contrast, Chat nav active state, and update summary.md
- theme.css: darken user bubble text (#3a3a4a light / #b0b0c8 dark) for better readability
- AppHeader: highlight Chat nav link when on any /chat/* route (was only active on exact /chat)
- summary.md: document workspace, graph, queue persistence, ToolCallCard direct-API, dedup scoping

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 18:00:04 -04:00
bvandeusenandClaude Sonnet 4.6 ffe1087aac Sync WorkspaceView queued bubble to indigo gradient (design language)
Matches the ChatView.vue update from the UI polish pass — queued bubbles
use linear-gradient(135deg, #6366f1, #4f46e5) and 18px border-radius
consistent with the Illuminated Transcript design language.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 17:33:54 -04:00
bvandeusenandClaude Sonnet 4.6 f5bee4573f Scope duplicate detection to same type (notes vs tasks)
create_note now only checks against existing notes (is_task=False);
create_task only checks against existing tasks (is_task=True). All three
checks (exact title, fuzzy title, semantic similarity) pass the type
filter through to list_notes and semantic_search_notes.

Adds is_task param to semantic_search_notes() so callers can restrict
results to notes or tasks independently.

Prevents a note titled "Design enemy AI" from blocking a task with the
same title, and vice versa.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 08:35:54 -04:00
bvandeusenandClaude Sonnet 4.6 da28a2f5c4 Fix queued bubble alignment and shape to match user messages
The role-user justify-content and message-bubble shape styles are scoped
to ChatMessage.vue and don't reach the inline queued bubbles. Add them
directly in ChatView and WorkspaceView: right-aligned row, 16px border
radius with 4px bottom-right corner, same padding and font as user bubbles.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 08:30:04 -04:00
bvandeusenandClaude Sonnet 4.6 a90342c9cb Restore semantic duplicate check at 0.90 threshold, 200-char minimum
Raises body character limit from 80 → 200 (skips short/stub notes)
and threshold from 0.87 → 0.90 (only flags near-identical content).

Enemy design notes sharing a template will pass through cleanly;
accidentally duplicated notes with the same detailed content will
still trigger the confirmation prompt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 08:20:20 -04:00
bvandeusenandClaude Sonnet 4.6 4dd3c1fe81 Fix queued bubbles, queue persistence, duplicate confirm UX, semantic check
- Queued message bubbles now match user bubble style (primary colour,
  right-aligned, opacity 0.45) in both ChatView and WorkspaceView
- Queue persisted to localStorage (fa_conv_queue_<id>); survives page
  refresh, restored on fetchConversation, cleared on delete
- ToolCallCard confirm/deny: buttons now inline in header row; "Create
  anyway" calls POST /api/notes or /api/tasks directly (no LLM round-trip);
  shows "✓ Created" / "Skipped" state; removed chatStore dependency
- POST /api/notes and POST /api/tasks now accept project (name string) and
  resolve to project_id server-side, matching tools.py behaviour
- Remove semantic similarity duplicate check from create_note and
  create_task — 0.87 threshold was too aggressive for topically-related
  notes; title-based exact/fuzzy checks are sufficient

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 08:11:39 -04:00
bvandeusenandClaude Sonnet 4.6 d7e1fe6aab UX: queue bubbles, dashboard queuing, duplicate confirm buttons, short-note fix
- Dashboard chat: allow typing/sending during streaming (queued in store)
  placeholder updates to "Type to queue…" during generation
- ChatView/WorkspaceView: replace queued chip with actual pending message
  bubbles — light grey, dashed border, "Queued" badge above content; clear
  button below the stack
- ToolCallCard: when tool returns requires_confirmation=True, show
  "Similar content found" label (not "Error"), link to the similar note,
  and "Create Anyway" / "Don't Create" buttons that auto-send the reply
- tools.py: fuzzy title match now returns requires_confirmation=True with
  similar_note data instead of a hard error, so numbered-series notes
  (Lore: X 0, Lore: X 1) can be created with one button click; semantic
  match responses also include similar_note for the link

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 23:32:21 -04:00
bvandeusenandClaude Sonnet 4.6 12644999c1 Settings: reorganise into tabs (General, Account, Notifications, Integrations, Data, Admin)
Replaces the single long scrolling page with a tab bar. Active tab
persists to localStorage across navigation. Admin tab only visible to
admins. Push notifications description clarified to mention HTTPS requirement.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 23:20:33 -04:00
bvandeusenandClaude Sonnet 4.6 4327b9f858 CI: use venv for Python tests to isolate setuptools from system Python
deadsnakes python3.12 sees system python3.10's setuptools at
/usr/lib/python3/dist-packages and skips installation, leaving python3.12
without setuptools. A venv gets its own site-packages so pip/setuptools
are properly present for http-ece's legacy setup.py build.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 22:47:08 -04:00
bvandeusenandClaude Sonnet 4.6 2b4163523b CI: pre-install http-ece with --no-build-isolation to fix PEP 517 isolation
pip builds legacy setup.py packages in an isolated environment that doesn't
inherit the globally installed setuptools. Pre-building http-ece with
--no-build-isolation bypasses that isolation so setuptools is available.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 22:34:12 -04:00
bvandeusenandClaude Sonnet 4.6 52d14672e5 CI: install setuptools before dev deps to support legacy http-ece build
http-ece (pulled in by pywebpush) uses setup.py and requires setuptools,
which is not included in the deadsnakes Python 3.12 install.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 22:29:05 -04:00
bvandeusenandClaude Sonnet 4.6 090df1633a Fix ruff E741: rename ambiguous variable l → log in task_logs route
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 22:28:34 -04:00
bvandeusenandClaude Sonnet 4.6 690270519f Fix push notifications: focus suppression, empty body, error visibility
- sw.js: suppress notification when the target chat tab is already focused
  (clients.matchAll visibility check before showNotification)
- generation_task.py: provide meaningful body for tool-only responses
  (lists tool names instead of sending an empty string that browsers discard);
  promote scheduling failure from debug to warning
- push.py: promote send errors from warning to error with exc_info;
  log successful sends at INFO so they're visible in normal operation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 22:12:20 -04:00
bvandeusenandClaude Sonnet 4.6 059a2e06d5 Queue concurrent prompts instead of dropping or conflicting
Chat store: when sendMessage() is called while streaming, push to a
per-conversation queue and return. When the stream ends the next queued
message fires automatically via setTimeout(0) to keep the call stack
clean. clearQueue() and queuedCount exposed for UI consumption.
Queue is cleaned up on conversation delete.

ChatView/WorkspaceView: remove the streaming guard from the local
sendMessage() functions and the :disabled on the textarea so users can
type and submit freely while streaming. Input placeholder changes to
"Type to queue next message…" during streaming. A small " N queued ×"
chip appears below the streaming bubble showing queue depth with a
cancel button.

DashboardChatInput: disable input, attach button, and send button
during streaming. The dashboard creates a fresh conversation per
message so in-conversation queuing doesn't apply — locking the input
is the correct UX here. Placeholder updates to "Generating response…"
during streaming.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 22:05:49 -04:00
bvandeusenandClaude Sonnet 4.6 e34f6d6e54 Bump all GitHub Actions to latest major versions
actions/checkout v4 → v6
actions/setup-node v4 → v6  (Node 24 runtime, fixes punycode warning)
docker/setup-buildx-action v3 → v4  (Node 24 + ESM)
docker/login-action v3 → v4  (Node 24 + ESM)
docker/build-push-action v6 → v7  (Node 24 + ESM)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 21:41:14 -04:00
bvandeusenandClaude Sonnet 4.6 bb41ce48be Fix Python CI jobs: drop container, install Python 3.12 directly
python:3.12-slim has no Node, causing actions/checkout@v4's post-step
to fail with 'exec: node not found'. Drop the container: declaration
so jobs run on the ubuntu-latest runner (which has Node). Install
Python 3.12 via deadsnakes PPA for the test job since the runner base
image (Ubuntu 22.04) ships with Python 3.10.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 21:33:11 -04:00
bvandeusenandClaude Sonnet 4.6 c65aad6639 Upgrade all major frontend dependencies
- TipTap 2 → 3: Extension from @tiptap/core, Placeholder from
  @tiptap/extensions, TaskList/TaskItem from @tiptap/extension-list,
  link: false in StarterKit (now bundles Link), @tiptap/core added
- marked 15 → 17: heading renderer updated to tokens/parseInline API
- Pinia 2 → 3, Vue Router 4 → 5 (no code changes required)
- Vite 6 → 7, @vitejs/plugin-vue 5 → 6, vue-tsc 2 → 3
- TypeScript 5.6 → 5.9: fixed Uint8Array<ArrayBuffer> strictness in
  push.ts, removed unused bodyEl ref in NoteViewerView.vue
- .npmrc: legacy-peer-deps=true for TipTap v3 peer dep resolution

TipTap 3 new capabilities now available: static renderer
(createStaticRenderer), MarkViews, @tiptap/extensions package.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 21:30:55 -04:00
bvandeusenandClaude Sonnet 4.6 8b96115bb2 Bump vue to 3.5.30 (patch)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 19:57:33 -04:00
bvandeusenandClaude Sonnet 4.6 c6211e5239 Gate Docker build on CI passing
Merged ci.yml and build.yml into a single workflow. The build job now
declares needs: [typecheck, lint, test] so images are only pushed when
all checks are green. PRs run CI only; branch/tag pushes run CI then
build if successful.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 18:53:58 -04:00
bvandeusenandClaude Sonnet 4.6 3d861d08eb Skip semantic duplicate check for bare-title tasks/notes
The semantic similarity check was flagging unrelated short-title tasks
as duplicates (e.g. "Lore: Shell 0" matching "Lore: Reinitialization 0"
at 91%) because with no body, the embedding is purely title-based and
co-domain tasks in the same project share a tight embedding neighborhood.

Only run the semantic check when the body is ≥ 80 chars — enough
content to make a meaningful comparison. The fuzzy title check already
covers exact/near-exact title duplicates.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 18:49:25 -04:00
bvandeusenandClaude Sonnet 4.6 bd9ba9b526 Fix scanner probes returning 200 via SPA catch-all
The 404 handler was unconditionally serving index.html (200) for all
non-API, non-static paths, including scanner probes for .php, .asp, .cgi
etc. Added _SPA_EXTENSIONS set so paths with unknown extensions get a
real 404 instead of a misleading 200.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 18:49:25 -04:00
bvandeusenandClaude Sonnet 4.6 46672725a1 Skip semantic duplicate check for bare-title tasks/notes
The semantic similarity check was flagging unrelated short-title tasks
as duplicates (e.g. "Lore: Shell 0" matching "Lore: Reinitialization 0"
at 91%) because with no body, the embedding is purely title-based and
co-domain tasks in the same project share a tight embedding neighborhood.

Only run the semantic check when the body is ≥ 80 chars — enough
content to make a meaningful comparison. The fuzzy title check already
covers exact/near-exact title duplicates.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 18:42:07 -04:00
bvandeusenandClaude Sonnet 4.6 4d55d9d82a Fix scanner probes returning 200 via SPA catch-all
The 404 handler was unconditionally serving index.html (200) for all
non-API, non-static paths, including scanner probes for .php, .asp, .cgi
etc. Added _SPA_EXTENSIONS set so paths with unknown extensions get a
real 404 instead of a misleading 200.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 18:28:25 -04:00