Commit Graph

58 Commits

Author SHA1 Message Date
Torres
2160ba6ce8 feat(v1.1): item 9 — batch formula v2 compose rewrite (AMENDS the frozen '<=5 returns' rule)
Allocator now: reserved returns (>=2 whenever eligible stock exists,
role order, surprising-gate on reserved nope slots, cap-4 family) ->
wildcards (2) -> fresh (remainder, capped at 23; wilds draw from the
fresh pool without consuming fresh slots) -> fill returns to size (role
order, <=2 re-nopes total, two-pass family cap: 4 preferred, 8 rescue for
fill slots only). MAX_RETURNS dies; FRESH_CAP=23, MIN_RETURNS=2,
RETURN_FILL_FAMILY_CAP=8 land. Rich day = 21 fresh + 2 second looks +
2 wildcards (the sign-off shape); dry day fills to 25 with returns; 0/0/0
composes an empty section; shortfalls are never padded.

Docs: module docstring, RANKING.md section recipe and DATA-MODEL returns
eligibility rewritten to the v1.1 contract (+ rubric_debug pref noted).

Selfcheck: A1-A8 compose matrix on deterministic staged pools (21/2/2
rich; 5 fresh + 20 SL fill incl. wild-from-fresh derivation; 21/2/2
sign-off; 12-return dry with <=2 nopes + LAST_TWO re-compose; 40 fresh ->
23+2 then the 15 leftover with zero overlap; 0/0/0 empty; family-cap 8
fill rescue; rested/gate-blocked re-nope rules). Pool-honest note: the
spec's A2 '5 fresh + 18 returns + 2 wild' assumed 7 fresh listings —
wildcards are by definition never-swiped, so a 5-fresh day reads 5 fresh
total (3 new + 2 wild) + 20 second looks.
2026-11-13 13:53:33 -04:00
Torres
a1455aade4 feat(v1.1): items 7-8 — fresh counter pill + loud 'No more new bikes!' empty state
Item 7: /api/stats gains fresh (actives with no swipe row — the deck's
new-pool predicate verbatim); the header pill reads 'N live · M fresh'
(new badge unchanged); old APIs without fresh render no segment.

Item 8: sections.pool_counts() answers can-the-forge-serve-anything from
the composer's own candidate SQLs (stock answer, LAST_TWO not applied)
and rides every section_state body. The FE adds #empty2 ('No more new
bikes!' + 08:30 line + undo note): shown whenever the pile/buffer drain
and pool is 0/0/0 (report masked), on a zero-item dry forge too; undo
hides it; legacy .empty untouched. UpdateUI repaints the caption per
hydrate; the stats key-set selfcheck gains fresh.
2026-11-13 13:42:08 -04:00
Torres
0d2e5a592f feat(v1.1): item 6 — dry-catalog messaging (forged batches with 0 fresh are labeled)
section_state now mirrors the composed recipe at body.recipe; the forge
note appends 'catalog fully reviewed' when the batch has zero fresh cards.
The FE shows the BACKLOG sentence in the report (dryCap) and in the forge
status line whenever recipe.new===0 with cards served; a non-dry forge
behaves exactly as before. No new fetch loops.
2026-11-13 13:39:22 -04:00
Torres
98d345ceee fix(v1.1): item 5 — section counter keys off in-section events (2/2 -> 3/2 bug)
A card is decided only when a swipe event carries THIS section's id;
the lifetime swipes.action a second look wears into its section no longer
paints it as pre-decided. progress/undecided, report counts (latest
in-section event per listing) and maybe_exhaust all use the new
section_decided_ids() set; read path only, no writes. Every section_state
item carries decided_this_section; the FE counts server truth unless a
local intent is in flight, and sectDecided is clamped to sectTotal
everywhere (commit + paintSect) so the pill can never read past N/N.

Semantics note: completing a section now means striking EVERY item in it
(returns re-struck on their second look) — the exhaust selfcheck was
updated to strike all items, matching the deck's real flow. Undo-on-a-
return residual (event stays after the DELETE) is the accepted Q5 clamp-
only behavior, covered by a dedicated selfcheck case.
2026-11-13 13:38:18 -04:00
Torres
ac613e680a feat(v1.1): item 4 — minimal rubric (gauge + %) and rubric_debug 'why' toggle
- prefs API accepts the new rubric_debug key (off|on), the only new runtime
  write in v1.1; every section_state body now carries prefs.rubric_debug.
- fillRank renders the tank gauge + rounded % by default; the breakdown
  lines, audit footer and the quiet listing-enrichment fetch run only when
  the pref is on. List rows and the card face stay rubric-free (unchanged).
- Trends 'Actions' gains a 'Why breakdown: off | on' row in the setMode
  style; updateSectionUI repaints it on every section hydrate.
2026-11-13 13:31:21 -04:00
Torres
e67f8a0cda feat(v1.1): items 1-3 — keyboard map, info-first tap flow, photos = fit + blurred backdrop
Item 1: ArrowLeft/Right/Up drive nope/like/maybe through commit(); ArrowDown
and Backspace (kept alias) drive undo(); Esc closes the top open sheet only
(viewer -> dossier -> deck, inert on report). Text inputs/TEXTAREA/SELECT/
contentEditable swallow keys; arrow defaults are prevented (no page scroll).

Item 2: card center tap now opens the dossier (bike data first); side taps
keep flipping photos; the direct card -> viewer door is retired. The deck
dossier records its pile entry; the hero tap opens the viewer with a
backToDetail chain so Esc walks viewer -> dossier -> deck (repeatable).

Item 3: deck card + dossier hero photos switch from cover to contain over a
soft blurred backdrop of the same image (option C, demo panel C); thumb and
fullscreen viewer untouched; nophoto paths render no backdrop.
2026-11-13 13:30:09 -04:00
Torres
7a1d14f23a docs: v1.1 build authorized — Glenn's sign-offs (>=2-SL default, violet band, Backspace alias); status -> implementation 2026-11-13 13:22:16 -04:00
Torres
829d43851b docs: v1.1 implementation specification 2026-11-12 15:04:40 -04:00
Torres
381b6c8a9c docs: v1.1 round 2 — fresh counter, loud empty state, batch formula v2 (25-target, second-look fill), Second Look band pop 2026-11-12 14:51:43 -04:00
Torres
7e4c41cffd docs: v1.1 gains section-counter bug fix + dry-catalog messaging (diagnosed live 2026-11-12) 2026-11-12 14:44:07 -04:00
Torres
d1c3adddff docs: version map — v1.1 (QOL batch, awaiting build order) / v1.2 (analytics suite + fresh-ledger reset) 2026-11-12 14:39:07 -04:00
Torres
085b2bc2ce docs: BACKLOG — v1.1 photo treatment decided: option C (fit + blurred backdrop) for card + info hero 2026-11-12 14:37:31 -04:00
Torres
e77ed38ebf docs: BACKLOG — gallery mirror gap (galleries not locally hedged; primaries only) 2026-11-12 14:35:15 -04:00
Torres
eff1ae313f docs: BACKLOG — v1.1 QOL wishlist (shortcuts, tap flow, photo fit, rubric) + analytics phase notes 2026-11-12 14:31:29 -04:00
Torres
47cecbc28b docs: record v1.0.0 Gitea release (tag on main head) 2026-11-12 14:17:38 -04:00
Torres
d4cb6bc2e8 docs: P4 complete — tailnet sidecar authorized, biketinder node serving, first phone swipes; exit criteria met 2026-11-12 14:06:09 -04:00
Torres
e1db51c461 docs: P4 deployed — stack live under podman-stack-bike-tinder.service, nightly import+read+backup armed, in-container requests fix, sidecar pending one-time tailnet auth 2026-11-12 10:03:54 -04:00
Torres
6ed8eb5f06 deploy fix: requirements — add requests (agent-pass live call imports it; the slim image surfaced the gap: ModuleNotFoundError 500 on /api/agent/run in-container) 2026-11-12 10:01:55 -04:00
Torres
d663f1eeb5 deploy: P4 runbook — deploy/rollback/logs/import gating/backup-restore drill/phone checklist + one-time sidecar key step 2026-11-12 09:59:45 -04:00
Torres
2d9cae2624 deploy: P4 stack kit — docker-compose (app + Tailscale sidecar, userspace), systemd unit (Delegate=yes), Dockerfile on 3.14-slim with --state-dir, serve.json, gitignore stack-local .env/state 2026-11-12 09:58:52 -04:00
Torres
8a0c566a68 docs: backlog — dry-pool UX + agent feedback notes (2026-11-12) 2026-11-12 09:38:00 -04:00
Torres
5226ec4819 fix(agent): /api/agent/run defaults to live when OPENROUTER_API_KEY is configured (was always mock) 2026-11-12 09:38:00 -04:00
Torres
44202c3679 docs: B1 receipts — confirmed the 1 un-enriched item is a photo-less ad at source (placeholder by design) 2026-11-11 00:38:57 -04:00
Torres
a50ed1e593 docs: B1 receipts — gallery counts corrected (639/640 carry imgs; 1 un-enriched falls back to legacy single image) 2026-11-11 00:38:38 -04:00
Torres
7441f0550b docs: B1 shipped — photo experience v2 built, verified (walk 390/390, selfcheck 31/31), live on :9453 (2026-11-11) 2026-11-11 00:37:54 -04:00
Torres
036a5116b8 b1: README — B1 develop notes (columns, importer, /img?i= route, mirror naming, payload fields, FE gestures, fe_walk_b1 usage, test data paths) 2026-11-11 00:21:15 -04:00
Torres
4630813644 b1: frontend fix — viewer segment bars render flat inside #vsegs (no nested .segments); fe_walk_b1: deterministic headless §11 evidence harness (fixture DB 701-706, staged local photos, real pointers: flips/clamps/viewer/zoom/pull-close/rotation/fast-chain/report/forge/list/trends) 2026-11-11 00:20:16 -04:00
Torres
38271d0d88 b1: selfcheck — 6 new B1 checks (guarded migration, imgs import + idempotence, 640-row copy byte-stable re-import, /img?i= chain, swipe photo_idx persistence, payload n_photos/last_photo_idx + rotation math); hardened two P3 checks against live-data drift on migrated copies (event seeding + profile sources); payload contract now includes B1 keys 2026-11-11 00:10:56 -04:00
Torres
fff9f05bae b1: frontend §11 — tap zones (sides flip clamped, center fullscreen, ⓘ wins), segment bars + band top:22 amendment, fullscreen viewer (swipe nav clamp, pinch 1-4x + double-tap zoom, ✕/pull-down close, same-position return), repeat rotation lead (last+1)%n, photo_idx on swipe commits, photo preload, sw cache v3 2026-11-11 00:08:04 -04:00
Torres
049b4fd280 b1: imgfetch — gallery mirroring: imgs[n] -> {id}_{n}.webp (primary n=0 keeps legacy {id}.webp, dedup by dst), per-file CLI progress, resumable/skip-existing as P2 2026-11-11 00:04:56 -04:00
Torres
604b983a51 b1: api — /img/{id}?i=n gallery chain (mirrored file -> CDN 302 -> 404; i=0 legacy), payload n_photos + last_photo_idx on deck/list/listing/section rows, POST /api/swipe persists optional photo_idx 2026-11-11 00:04:10 -04:00
Torres
4ca4cacda0 b1: importer — carry hunt imgs gallery into listings.imgs (JSON, page order); img_url falls back to imgs[0] 2026-11-11 00:03:03 -04:00
Torres
3b64635339 b1: db — guarded migrations: listings.imgs (gallery JSON) + swipes.last_photo_idx 2026-11-11 00:02:42 -04:00
AlexanderRozhenko
6de2fcc1bd docs: B1 (photo experience v2) contract frozen — UI-SPEC §11 (galleries, photo flip, fullscreen viewer, repeat rotation), DATA-MODEL B1 additions; PLAN/BACKLOG: B1 pulled forward before Phase 4 2026-11-10 23:57:33 -04:00
AlexanderRozhenko
8c1946db8e docs: PLAN — Phase 3 built, verified, and live (Torres build $3.80/25-25; Alex independent verification; second-look fix 1e4d840; live integration 2026-11-11 + calibration pass) 2026-11-10 23:41:49 -04:00
Torres
1e4d84017a frontend: fix second-look returns filtered out of the swipe buffer (decision===null filter dropped them; returns must enter the deck to be re-struck) 2026-11-10 23:38:55 -04:00
Torres
b6e1851b11 frontend: report-screen exhaustion fix — local decision map drives section progress; show report on local exhaust with rate-limited resync (verified headless: full 25-card section loop, forge, trends, tanks) 2026-11-10 23:28:06 -04:00
Torres
c205d315b7 docs: README — Phase 3 status, agent-pass + copy-hygiene develop notes, new routes 2026-11-10 23:18:26 -04:00
Torres
04463cadcd P3: personalization core — event log + taste engine + section engine + agent pass + API + frontend
- db.py: guarded P3 tables (swipe_events/sections/section_items/taste_profile/
  agent_runs), one-time event seeding from swipes, append_event round math
- taste.py: deterministic taste engine (action x round x recency weights,
  n/(n+3) shrink, ramp w=min(.85,N/60), percentile norm, agent adjustments
  +/-10, points + source-labeled breakdown + tank bands)
- sections.py: compose/read/exhaust/forge — 2 wildcards + <=5 returns
  (<=2 nopes, surprising only) + remainder new, <=4/family, >=3 price
  bands w/ repair, returns maybe>like>nope, last-two-section exclusion,
  ondemand/strict modes
- agentpass.py: mock (deterministic) / live (OpenRouter, one retry) /
  fallback; strict payload schema; --print-summary/--dry-run
- api.py: GET /api/section, POST /api/section/forge, GET /api/trends,
  POST /api/agent/run, POST /api/prefs; P3 payload fields (points, tank,
  slot, round_next, points_breakdown); events stamped with section_id
- frontend: section buffer + progress pill, report screen + forge + mode
  toggle, WILDCARD/SECOND LOOK bands, tank gauges (list + dossier), dossier
  Why-this-rank, real Trends tab, /api/deck fallback for pre-P3 API, sw v2
- selfcheck: 9 new P3 checks (25/25 green)
2026-11-10 23:18:26 -04:00
AlexanderRozhenko
8060852b83 docs: backlog - Glenn's photo-experience v2 idea queue (multi-image galleries, Tinder-style photo swipe, fullscreen viewer) 2026-11-10 23:12:29 -04:00
Alex (Hermes)
40b42b1d40 docs: freeze Phase 3 contract - sections (25), second looks with rounds, match points + tank gauge, agent pass; UI-SPEC v3 + DESIGN/RANKING/DATA-MODEL/PLAN additions 2026-11-10 22:52:51 -04:00
AlexanderRozhenko
49e284ff56 docs: record Phase 2 build + verification (dedupe/backfill resolved, proof URLs) 2026-11-10 21:43:33 -04:00
Torres
d7fb694626 importer: dedupe refinement — min(ns,nb) completeness pairing (stretch I)
- cross-province groups now pair ns<->nb by completeness (img > km > older)
  up to min per region instead of collapsing the whole group to one row;
  surplus siblings (multi-unit dealer ads) stay active — no false merges
- verified on live DB: dup 80 -> 77, active 560 -> 563, total 640; the 3
  freed rows are the BMW G 310 R dealer siblings (450/451/452); deck top
  order unchanged (Indian Scout 91 x3 then Vulcan 900 73); re-import
  idempotent (77 stable)
- selfcheck +2: multi-unit pairing semantics, reversibility (flip to
  active + re-import re-derives the pairing) — 16/16 green
2026-11-10 21:25:02 -04:00
Torres
8367e2b7c8 deploy/Dockerfile (python:3.13-slim app contract) + README run/develop section
- Dockerfile: 3.13-slim, pip install requirements.txt, copy app/, EXPOSE
  8000, serves /data/bike-tinder.sqlite3 + /data/img on 0.0.0.0:8000
  (compose/systemd/tailscale stay with the parent)
- README: status line now reflects Phase 2; added Run & develop (venv,
  importer, imgfetch incl. --limit, serve, selfcheck, deck CLI, route map)
2026-11-10 21:23:48 -04:00
Torres
778d1bfd4b selfcheck: extend with P2 API checks on a temp DB copy (14 total)
- 8 P1 spine checks unchanged; 6 new API checks via fastapi.testclient
  against create_app() on a temp COPY of the live DB (live file never
  opened by tests): root/static, stats-live==active+health, deck page 1
  monotonic + undecided-only + full cursor walk zero-overlap vs SQL truth,
  swipe like->list->edit->delete lifecycle (+invalid payload rejection),
  /img/{id} cached-200/hotlink-302/none-404, badge roundtrip
- bare-python run keeps the 8 P1 checks and notes the skipped P2 set
- known framework quirk documented: this fastapi+httpx TestClient pair
  mangles RedirectResponse to a bare 404; real uvicorn serves the true 302
  (proof run verifies over the wire)
2026-11-10 21:22:26 -04:00
Torres
57fae0df49 app: no-build PWA frontend (fidelity port) + image mirror
- app/static/index.html: single-file app — DOM/CSS/stack engine ported
  verbatim from reference/mockup-001-fidelity.html (DEPTH/SC/TY/DIM, drag/
  fly/undo, one .topcard, no-lock commits, stamps, dossier sheet,
  pull-to-close, empty state); production deltas only (UI-SPEC §7): deck
  buffer + cursor refill (40/page), optimistic swipe persistence with
  2s/5s/10s/15s retry queue + toast, badge-once-per-session, real list
  filters + search (server-side), real Matches screen, /img/{id} everywhere
  with placeholder fallback, SW registration, safe areas, desktop centering
- manifest.json + sw.js (precache shell, network-first API, cache-first imgs)
- icons: 192/512/maskable/apple-touch-180, dark tile with the brand
  accent-gradient mark (Pillow)
- app/imgfetch.py: local mirror, hunt's Chrome-120 UA, ~1s pacing,
  skip-existing, .part writes atomic, per-item failure isolation,
  fetched/skipped/failed summary; verified: --limit 15 -> 15 fetched, full
  run 5 more, re-run 0 fetched 20 skipped (20/20 mirrored)
2026-11-10 21:21:27 -04:00
Torres
5df2a20bac app: FastAPI API + serve CLI (Phase 2)
- create_app(db_path, img_dir, state_dir) factory; every route + payload
  from app.state — selfcheck runs the real router on a temp DB copy
- /api/health (ok/db/counts), /api/stats (live/new/likes/maybes/nopes/
  last_import), /api/deck (undecided semantics, score/first_seen/id DESC,
  opaque '<score>|<first_seen>|<id>' cursor, strictly-after tuple page,
  remaining count), /api/swipe upsert + DELETE (idempotent), /api/list
  (decision/sort/q/limit/offset, total), /api/listing/{id}, /api/import
  (importer, HTTP 200 even when gates fail), /api/badge/seen, /img/{id}
  (cached file -> 302 img_url -> 404), static routes (/, manifest, sw.js,
  icons, apple-touch; no-cache on index + sw.js)
- frozen payload contract incl. img=/img/{id} when img_url or cached file
- serve.py: argparse (--db/--img-dir/--state-dir/--host/--port, defaults
  127.0.0.1:9453) -> uvicorn.run
- verified: 560-deep deck walks clean with zero page overlap, swipe ->
  list -> edit -> delete -> back on deck, badge roundtrip (new 560 -> 0);
  real uvicorn returns true 302 for the img fallback (TestClient in this
  fastapi/httpx pair mangles RedirectResponse into a 404 — framework
  artifact, not app behavior)
2026-11-10 21:17:57 -04:00
Torres
02a6b6df7e app: P2 migrations (year/seller_type/extras) + importer backfill, guards idempotent
- db.init_db gains guarded ALTER TABLE additions for listings.year /
  seller_type / extras (schema.sql v1 untouched; PRAGMA-checked idempotent)
- rubric: extras_from_text() returns matched EXTRAS labels (table order)
- importer: normalize computes year (existing helper), seller_type ONLY
  from a real state-item signal (dealer/private, else NULL), extras JSON;
  upsert rewrites the new columns on both paths
- verified on live DB: import idempotent, counts stay 640/560/80, year
  backfilled on 599 rows, extras on 45, seller_type 0 (no state signal)
2026-11-10 21:13:40 -04:00
AlexanderRozhenko
9bc6948861 docs: freeze Phase 2 contract — UI-SPEC v2 (approved mockup A, stack engine, deltas) + DATA-MODEL P2 addendum (migrations, cursor pagination, media mirror) + frozen mockup reference 2026-11-10 21:11:27 -04:00
AlexanderRozhenko
d8510c184a docs: record verified Phase 1 results (twin counts in SOURCES; phase status + Phase 2 backlog in PLAN) 2026-11-10 19:00:00 -04:00