Add project memory handoff notes

This commit is contained in:
2026-09-14 15:32:55 +02:00
parent c6040a8cb2
commit d43b8d5f60
+66
View File
@@ -0,0 +1,66 @@
# Project memory — time_track_server
Handoff notes for agent conversations. Verify claims against current code and Git state before relying on them. Last updated: 2026-09-14.
## Current focus
The combined-timesheet server is deployed and in daily use. Real data flows from both local trackers via systemd timers. Remaining feature work: mobile-provider PDF call parser (blocked on a redacted sample PDF), optional Ollama enrichment (design only so far).
## Architecture
- Outer repo (this one): FastAPI server (`app/`) + Docker Compose deployment + two Git submodules.
- `time_track/` — Waybar tracker (submodule, origin `http://100.103.83.12:3003/fegger/waybar_time_track.git`)
- `zed_time_tracker/` — Zed hours tracker (submodule, origin `.../fegger/zed_time_tracker.git`)
- Server: `app/main.py` (routes), `app/services.py` (ingest/derivation/grouping), `app/models.py` (`RawEvent`, `TimeEntry`, `CallImport`), `app/templates/` (Jinja + Pico CSS), `app/database.py`.
- Access: `http://100.103.83.12:3008` over Tailscale; `BIND_IP` from `.env`; Postgres internal-only; secrets only via `.env` (never commit `.env`).
- Deploy on server: `git pull && docker compose up --build -d` (submodules first: commit each submodule, push, then update pointers in outer repo, push all three).
- Backup: `docker compose exec -T postgres pg_dump -U time_track time_track > backup.sql`.
## Data semantics (must preserve)
- Timezone: day grouping, display, and monthly reports use `Europe/Vienna` (`TZ` in `services.py`); timestamps stored UTC-aware.
- Sources: `time_track` (explicit intervals → `accepted` on ingest), `zed` (heartbeat-derived blocks → `suggested`), `manual` (web-created → `accepted`), commits stored as `RawEvent(source="zed_commit")`, displayed per day, never time.
- Unique key `(source, device_id, external_id)`; Zed block `external_id` = `stable_id(device, project_slug, start, end)` — heartbeat re-derivation must skip existing IDs so user accept/reject decisions stick (fixed 500 bug on 2026-09-14).
- **Overlap rule (user decision 2026-09-14):** suggested entries overlapping accepted time are evidence only — "Accept day" skips them, UI hides Accept, `POST /entries/{id}/status` returns 422. Never let inferred time double-count.
- Accepted `zed_inferred` becomes `kind="manual"` (durable, survives re-derivation).
- Rejected entries are excluded from exports; suggested time always reported separately from accepted.
- Monthly export `GET /export/monthly?month=YYYY-MM&format=md|csv`; full calendar month, Vienna dates, month-boundary-safe.
- `DATABASE_URL` is built via `SQLAlchemy URL.create` from `POSTGRES_*` env parts — never go back to string interpolation (passwords with `@ : / # %` broke it).
## Client sync (this workstation, verified working)
- `zed_time_tracker/sync.py` (per project, offset-based state in `.zed-hours/sync-state.json`, strips absolute paths) and `sync_all.py` (multi-project wrapper reading `~/.config/zed-hours-sync/{env,projects.txt}`).
- `time_track/time_track.py sync` (re-sends all finished entries; server dedupes by entry UUID).
- systemd user timers on this workstation (`framework`): `zed-hours-sync.timer` + `time-track-sync.timer`, every 15 min, `Persistent=true`; env files `~/.config/{zed-hours-sync,time-track-sync}/env` hold real tokens (mode 600 — never print or commit them). Both verified syncing successfully on 2026-09-14 (time_track: 15 entries; zed: 0 new, state up to date).
- Only project configured in `projects.txt`: `/home/fegger/Code/ixsol/gem360-git` (slug `gem360-git`).
## Decisions & conventions
- Single user; no rates/invoicing — duration reporting only; call logs are mobile-provider **call-detail records** (logs only, no audio).
- Zed model choice for optional enrichment: `qwen3:8b` at `http://100.103.83.12:11345` (env `OLLAMA_*` reserved; no Ollama code yet — deterministic parsing first).
- Call import pipeline is upload + text extraction (`pdfplumber`, `pypdf` fallback, `needs_ocr` status) only; provider-specific parser intentionally deferred until a redacted sample PDF is provided.
- Auth: single password (`ADMIN_PASSWORD`) for web, `SYNC_API_TOKEN` bearer for sync APIs; unauthenticated browser requests redirect to `/login` (303), APIs return 401.
- Commits: repo hygiene — commit only when asked (user has approved commit+push each iteration); keep `.env` and tokens out of everything.
## Open items / blockers
- **Call-log PDF parser**: needs a redacted mobile-provider sample statement (columns/layout/duration format) before implementation. Upload UI already collects files to the `call_imports` volume.
- **Ollama enrichment**: planned worker step for suggesting task/project from provider labels; output must be suggestion-only, strict JSON (Pydantic), minimal PII sent.
- No HTTPS (Tailscale encryption only); add reverse proxy/Tailscale Serve if browser HTTPS is wanted.
- User timers run only while the desktop session is active; `loginctl enable-linger` would extend to logout (not enabled — needs user decision).
- Known harmless diagnostics: naive `datetime.now()` warnings in tracker scripts (local-time JSON format is deliberate), FastAPI `Depends`-default style warnings, broad `except` in PDF fallback (intentional), unsorted-import lints in `main.py`; stale "group_*_by_day not found" warnings in `app/main.py` are false positives (functions exist in `services.py`, exercised in smoke tests).
- `zed_time_tracker/` shows ` ?` in `git submodule status` from an untracked `__pycache__/` (regenerated each run; harmless). Note: `time_track` repo has a *tracked* `__pycache__/time_track.cpython-314.pyc` — do not delete it.
## Files that matter now
- `app/main.py`, `app/services.py`, `app/models.py`, `app/schemas.py`, `app/templates/index.html`
- `zed_time_tracker/sync.py`, `zed_time_tracker/sync_all.py`, `time_track/time_track.py` (sync section ~L267)
- `docker-compose.yml`, `.env.example`, `README.md`
## Validation workflow (proven)
1. `python3 -m py_compile app/*.py time_track/time_track.py zed_time_tracker/*.py`
2. `docker compose build` / `config --quiet` with inline env vars (never create a local `.env` in the repo)
3. Full smoke test on `BIND_IP=127.0.0.1` with a hostile password (e.g. containing `@ : # / %`), seed data via sync APIs, exercise login + UI + exports with curl, verify idempotent re-syncs
4. Always end with `docker compose down -v --remove-orphans` and remove temp files (cookies, reports)
5. Commit submodule repos individually, update outer pointers, push all three