TaskFlow: Windows-first Tauri 2 desktop app (Rust core in src-tauri/, React/TS frontend in src/, Vite + pnpm) with a Python FastAPI sidecar (sidecar/, port 7878) doing AI summarize/filter/embed. It captures screen activity via UI Automation, with a local OCR fallback for windows UIA can't read, into SQLite (%APPDATA%\com.taskflow.desktop\taskflow.sqlite) and an Obsidian vault. macOS support is second-class: no window titles, idle detection stubbed.
cd src-tauri && cargo check --lib— primary verification. Do NOT runcargo build/pnpm tauri build: the full link hitsLNK1318(Windows PDB limit) on this machine.cd src-tauri && cargo test --lib— 34 tests (wiki ingest, workstream writer, daily index, dust cleanup, slug hygiene, fallback hygiene, rollup reassignment, OCR post-processing/clamping, capture backoff, retention cutoff/timestamp-format regression, snapshot teardown).pnpm build— frontend typecheck (tsc + vite). No lint/format/frontend-test config exists.- Sidecar deps: never
pip installinto system Python (numpy 2.x breaks torch). Use an isolated venv +uv pip install -r sidecar/requirements.txt. - Headless sidecar harness (boot, drive /summarize and /filter without the GUI): see
.agents/skills/run-taskflow/SKILL.md— read it before running or debugging the app; it is the verified source for setup, gotchas, and troubleshooting. Caveat: its.venv-sidecarsetup was authored but not yet verified end-to-end (see its TODO).
- Rollup engine is the memory spine:
rollup/scheduler.rsflushes ~10-min event windows →rollupstable → workstream nodes (TaskFlow/Projects/<slug>.md,## Activity Timeline). Daily notes are a derived index rebuilt from rollups (wiki/daily_index.rs) — workstream nodes are the source of truth.daily_index.rsis the sole writer ofMemory/Daily/<date>.md;generate_documentationdeliberately does not touch the vault for memory tasks (the old two-writers split was removed). - The vault graph is plain Obsidian wikilinks only (hub kinds: Apps/Sites/Activity/Projects + daily notes). No edge table, no ML entity extraction. Project resolution uses the
wiki_known_projectssetting (Settings → Capture → Known Projects); without it, a window-title heuristic mints junk project pages. Registry-gated cleanup deletes machine-generated dust workstream pages and reassigns their roll-ups to Inbox. - App hub slugs are lowercased and extension-stripped case-insensitively (
Explorer.EXE→Apps/explorer.md); project slugs preserve registry casing. - Technical AI errors (HTTP statuses, sidecar URLs) are logged, never written to the vault —
fallback_summaryonly accepts the genericFALLBACK_REASON_NO_AItext. - Obsidian itself is capture-excluded (
privacy.rs): the vault app is output, not an activity source (prevents recursive self-capture). - Sidecar runs from source in dev (
python sidecar/main.py): editingsidecar/*.pytakes effect on sidecar restart, no Rust recompile. - OCR is a last-resort fallback, not a screenshotter (
capture/ocr.rs): UIA readers run first; only when they all return None doesPrintWindowgrab the window's pixels in memory and run WinRTWindows.Media.Ocrover them. Bitmaps never touch disk — preserve that (it's the anti-Recall stance inPRODUCT.md). Gated bycapture_ocr_fallback(default on, Settings → Capture → Privacy), read per-capture from SQLite, and nested undercapture_screen_textwhich gates the whole deep-capture path. Windows above 2560px are downscaled (StretchBlt+HALFTONE), not cropped. - Deep-capture failures back off per app (
window_monitor.rs::backoff_delay, 30s doubling → 15m cap, reset on success) viaAppState::capture_failures. ANonecapture is ambiguous (unreadable / OCR off / privacy-rejected / still painting), so this must stay a self-healing breaker, not a blacklist. - Cloud API key in SQLite is XOR-obfuscated with
SHA256(hostname), not encrypted. Preserve the scheme or migrate it deliberately; don't treat the DB as secret storage. - Retention nulls
events.content, it does not delete rows (rollup/scheduler.rs::prune_raw_content, hourly): the row stays so the event feed and per-app stats keep working, while the verbatim screen text stops being retained. The cutoff ismin(now - raw_capture_retention_hours, rollup watermark)so a short retention can't starve the rollup engine — unless rollups are off, in which case the watermark is ignored (no future consumer). Build timestamp bounds in Rust (Utc::to_rfc3339()), never with SQLitedatetime('now', …):events.timestampuses'T'(0x54) anddatetime()returns a space (0x20), so same-day comparisons silently match nothing. There's a regression test for exactly this. - The
snapshotstable/FTS5/triggers from commit1e8adb4were schema-only (never read or written) and are now dropped on migration (schema.rs::drop_snapshot_artifacts, idempotent). The three orphanedeventscolumns it added (snapshot_id,correlation_id,metadata_json) are deliberately kept — SQLiteDROP COLUMNis fragile and they cost nothing. - Dead-by-design traps:
events.relevance(selected in queries, never written). Don't assume it works.
PRODUCT.mdis the design register: narrative-over-numbers (no charts/modals), keyboard-first, local-first, zero-friction capture. Check it before adding UI.- Vault output root is user-configured (
obsidian_vault_pathsetting); everything writes under<vault>/TaskFlow/. - Never commit
opencode.json— it contains a plaintext API key (currently untracked). - Workflow: feature branches (
v1-ui,engine-v2, ...) merged tomainvia PR.