docs: add CLAUDE.md with architecture, gotchas, and pending work
This commit is contained in:
parent
83b68ac435
commit
4d9a945dbd
1 changed files with 96 additions and 0 deletions
96
CLAUDE.md
Normal file
96
CLAUDE.md
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
# Snipe — Developer Context
|
||||
|
||||
> eBay listing monitor with seller trust scoring and auction sniping.
|
||||
|
||||
## Stack
|
||||
|
||||
| Layer | Tech | Port |
|
||||
|-------|------|------|
|
||||
| Frontend | Vue 3 + Pinia + UnoCSS + Vite (nginx) | 8509 |
|
||||
| API | FastAPI (uvicorn, `network_mode: host`) | 8510 |
|
||||
| Scraper | Playwright + playwright-stealth + Xvfb | — |
|
||||
| DB | SQLite (`data/snipe.db`) | — |
|
||||
| Core | circuitforge-core (editable install) | — |
|
||||
|
||||
## CLI
|
||||
|
||||
```bash
|
||||
./manage.sh start|stop|restart|status|logs|open|build|test
|
||||
```
|
||||
|
||||
## Docker
|
||||
|
||||
```bash
|
||||
docker compose up -d # start
|
||||
docker compose build api web # rebuild after Python/Vue changes
|
||||
docker compose logs -f api # tail API logs
|
||||
```
|
||||
|
||||
`compose.override.yml` bind-mounts `./tests` and `./app` for hot reload.
|
||||
|
||||
nginx proxies `/api/` → `172.17.0.1:8510` (Docker bridge IP — api uses host networking).
|
||||
|
||||
## Critical Gotchas
|
||||
|
||||
**Kasada bot protection:** eBay blocks `requests`, `curl_cffi`, headless Playwright, and all `/usr/` and `/fdbk/` seller profile pages. Only headed Chromium via Xvfb passes. `/itm/` listing pages DO load and contain a BTF (below the fold) seller card with "Joined {Mon} {Year}" — use this for account age enrichment.
|
||||
|
||||
**Xvfb display counter:** Module-level `itertools.cycle(range(200, 300))` issues unique display numbers (`:200`–`:299`) per `_get()` call to prevent lock file collisions when multiple Playwright sessions run in parallel.
|
||||
|
||||
**HTML cache:** 5-minute in-memory cache keyed by full URL. Prevents duplicate 15s Playwright scrapes within one session. Cleared on restart.
|
||||
|
||||
**SQLite thread safety:** Each concurrent thread (search + comps run in parallel) must have its own `Store` instance — `sqlite3.connect()` is not thread-safe across threads. See `api/main.py`.
|
||||
|
||||
**nginx rebuild gotcha:** nginx config is baked into the image at build time. After editing `docker/web/nginx.conf`, always `docker compose build web`.
|
||||
|
||||
**Playwright imports are lazy:** `sync_playwright` and `Stealth` import inside `_get()` — not at module level — so the pure parsing functions (`scrape_listings`, `scrape_sellers`) can be imported on the host without Docker's browser stack installed.
|
||||
|
||||
## DB Migrations
|
||||
|
||||
Auto-applied by `Store.__init__()` via `circuitforge_core.db.run_migrations`.
|
||||
Migration files: `app/db/migrations/001_init.sql`, `002_buying_format.sql`, `003_nullable_account_age.sql`
|
||||
|
||||
## Tests
|
||||
|
||||
```bash
|
||||
# Host (no Docker needed — pure parsing tests)
|
||||
conda run -n job-seeker python -m pytest tests/ -v --ignore=tests/test_integration.py
|
||||
|
||||
# In container
|
||||
./manage.sh test
|
||||
```
|
||||
|
||||
48 tests. Scraper tests run on host thanks to lazy Playwright imports.
|
||||
|
||||
## Trust Scoring Architecture
|
||||
|
||||
```
|
||||
TrustScorer
|
||||
├── MetadataScorer → 5 signals × 0–20 = 0–100 composite
|
||||
│ account_age, feedback_count, feedback_ratio,
|
||||
│ price_vs_market (vs sold comps), category_history
|
||||
├── PhotoScorer → phash dedup (free); vision analysis (paid stub)
|
||||
└── Aggregator → composite score, red flags, hard filters
|
||||
```
|
||||
|
||||
**Red flag sentinel gotcha:** `signal_scores` uses `None` for missing data; `clean` dict substitutes `None → 0` for arithmetic. Always check `signal_scores.get("key")` (not `clean["key"]`) when gating hard-filter flags — otherwise absent data fires false positives.
|
||||
|
||||
## Key Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `api/main.py` | FastAPI endpoint — parallel search+comps, serialization |
|
||||
| `app/platforms/ebay/scraper.py` | Playwright scraper, HTML cache, page parser |
|
||||
| `app/trust/aggregator.py` | Composite score, red flags, hard filters |
|
||||
| `app/trust/metadata.py` | 5 metadata signals |
|
||||
| `app/db/store.py` | SQLite read/write (batch methods) |
|
||||
| `web/src/views/SearchView.vue` | Filter sidebar + results layout |
|
||||
| `web/src/stores/search.ts` | Pinia store — API calls, result state |
|
||||
| `web/src/components/ListingCard.vue` | Listing card + auction dim style |
|
||||
| `web/src/assets/theme.css` | Central theme (CSS custom properties) |
|
||||
|
||||
## Pending Work
|
||||
|
||||
- **Keyword filtering** — must-include / must-exclude; negatives forwarded to eBay `_nkw` and applied client-side
|
||||
- **Seller enrichment** — background scrape of `/itm/` BTF seller card for account age (`account_age_days`)
|
||||
- **Bulk report UI** — multi-select listings → eBay report deep-link + CF community blocklist
|
||||
- **Snipe scheduling** — configurable bid-time offset, human approval gate
|
||||
Loading…
Reference in a new issue