b1: README — B1 develop notes (columns, importer, /img?i= route, mirror naming, payload fields, FE gestures, fe_walk_b1 usage, test data paths)

This commit is contained in:
Torres
2026-11-11 00:21:15 -04:00
parent 4630813644
commit 036a5116b8

View File

@@ -4,6 +4,8 @@ A private, Tinder-style web app for browsing the motorcycle market: swipe real l
**Status:** Phase 3 (personalization) built on `dev` (2026-11-11): sections of 25 (wildcards + second looks + new), taste engine with match points, tank gauges, dossier "Why this rank", real Trends tab, agent pass (mock / live / fallback). Phase 2 remains shipped as-is; deployment (podman stack) is Phase 4 — not started.
**B1 (photo experience v2)** built on `dev`: full photo galleries mirrored locally, deck flips (tap left/right thirds, clamped), segment bars, a fullscreen viewer (swipe nav, pinch/double-tap zoom, pull-down close), and repeat rotation (a re-shown card leads at its next photo). See UI-SPEC §11 + the "B1 develop notes" section below.
## How it works
```
@@ -87,6 +89,48 @@ Stage 2 + the task brief.
`data/` and `.venv/` are gitignored — the DB, image mirror and venv are local state.
## B1 develop notes (photo experience v2)
- **Columns** (guarded in `db.init_db`): `listings.imgs TEXT` (JSON array of
gallery URLs, page order; entry 0 = the primary = `img_url`) ·
`swipes.last_photo_idx INTEGER` (photo on screen at swipe time; drives §11.5
rotation `(last+1) % n`; NULL = photo 0).
- **Importer** copies the hunt state's `imgs[]` into `listings.imgs`; `img_url`
stays the state `img` and falls back to `imgs[0]`. Re-imports are
byte-stable (selfcheck proves it on the 640-row copy).
- **Route:** `GET /img/{id}?i=n` — mirrored file → CDN 302 → 404. `i=0`
(default) keeps the legacy chain (`{id}.webp`, then `{id}_0.webp`, then
`img_url`); `i>0` serves `{id}_{n}.webp` with the CDN fallback from
`imgs[n]`. Out-of-range/unknown → 404.
- **Mirror:** `app/imgfetch.py` downloads every `imgs[n]`. The primary (n=0)
lands at the legacy `data/img/{id}.webp` (one canonical file — no duplicate
fetch); extras land at `data/img/{id}_{n}.webp`. Same politeness as P2
(~1 req/s, skip-existing, `.part`-atomic, per-file CLI progress).
- **Payload:** deck/section/list/listing items add `n_photos` (int) and
`last_photo_idx` (int|null). `POST /api/swipe` accepts optional `photo_idx`
(non-negative int; persisted; absent key preserves, explicit null clears).
- **Frontend:** tap LEFT/RIGHT third = photo flip (clamped, no wrap, commits
nothing), CENTER third = fullscreen viewer (ⓘ still wins); segment bars per
photo (Tinder-style; band chips drop to `top:22px` when segments exist);
viewer = swipe nav + pinch 1×–4× + double-tap 1×↔2.5× + ✕/pull-down close
(returns to the same card/photo); re-shown cards lead at `(last+1) % n`;
preloads the top card's next photo + next card's lead; `sw.js` cache is
`bike-tinder-v3`.
- **FE walk (evidence):** `app/fe_walk_b1.py` — deterministic, headless:
```sh
.venv/bin/python -m app.fe_walk_b1 setup # fixtures: copy DB + 701-706 synthetics + local photos
env -u OPENROUTER_API_KEY .venv/bin/python -m app.serve \
--db /var/home/admin/dev/bike-tinder-b1/test-walk.sqlite3 \
--img-dir /var/home/admin/dev/bike-tinder-b1/test-img \
--host 127.0.0.1 --port 9454 # scratch server (mock forge)
.venv/bin/python -m app.fe_walk_b1 # 390 DOM checks
```
- **Test data:** `b1/test-state/` = hunt-state sample with `imgs` (8/3-photo
galleries; refresh `fetched_at` before an import gate test) ·
`b1/test-img/` = staged `{id}_{n}.webp` photos · `b1/test-copy.sqlite3` =
pristine live-DB snapshot (selfcheck copies it; never write to it).
## Deployment (planned)
A small podman compose stack on the Rotarran under `/var/mnt/DATA/podman-stacks/bike-tinder/`, its own systemd user unit (`Delegate=yes`), SQLite database. The stack ships its own Tailscale node (sidecar container — no host-tailnet coupling) and serves the app privately at `https://biketinder.tailf35a34.ts.net`. Single user; reachable only from Glenn's own devices.