# Ghost Node — Session Memory

> **What belongs here:** Current architecture state, key gotchas, reusable patterns, dev paths.
> **What does NOT belong here:** Session history (→ PROGRESS.md or ARCHIVE.md).
>
> ### ⚠️ MD Update Rule — DYNAMIC, NOT STATIC
> Only update the MD file(s) whose content was actually touched this session.
> Do NOT update all files after every task. Each file gets only what is relevant to it.
> - Update **this file** only when a new reusable gotcha, pattern, or architecture decision was made.
> - Update **PROGRESS.md** whenever any code file was changed.
> - Update **CLAUDE.md** only if API endpoints, DB schema, config keys, or architecture changed.
> - Move old PROGRESS.md session entries to **ARCHIVE.md** when they're no longer active context.

---

## Project Identity
- Ghost Node International Auction Sniper v2.7
- Owner: Abbas, Baghdad, Iraq
- Path: `C:\Users\Abbas\Documents\Downloads\ClaudeAuction2\`
- Backend: Python FastAPI (worker.py, 5000+ lines), port 8000
- NOT a git repository

---

## Frontend Stack (Current — Session 16, fully migrated)

- Location: `frontend/` directory
- Next.js 16.1.6, React 19, TypeScript, Tailwind CSS v4 (**NOT v3**)
- **Tailwind v4**: CSS variables via `@theme {}` in `globals.css` — NO `tailwind.config.ts`
- **Session 27 redesign**: CSS prefix changed from `ghost-*` to `g-*`. Colors: `--color-g-base` (#050510), `--color-g-green` (#00e87b), `--color-g-cyan` (#06b6d4), etc. Classes: `bg-g-base`, `text-g-green`. Components: `.g-card`, `.g-card-glow`, `.g-btn`, `.g-btn-primary`, `.g-badge`, `.g-table`, `.glass`, `.glass-strong`, `.gradient-text`, `.gradient-accent`. Backward compat aliases (`.card-panel`, `.btn-ghost`, `.btn-danger`, `.page-title`, `.page-subtitle`) still work.
- `output: 'export'` in next.config.ts → static build at `frontend/out/`
- Node.js: default installer `C:\Program Files\nodejs\` (on PATH). Alternative: portable at `C:\Users\Abbas\AppData\Local\nodejs-portable\node-v22.14.0-win-x64\`

### Key Architecture Decisions
- SSE route disabled for static build (`app/api/stream/route.ts.disabled`)
- All API files use `http://localhost:8000` as BASE (full URL, not relative)
- FastAPI serves Next.js static build when `frontend/out/` exists
- `if _frontend_out.exists():` guard is safe — `dashboard.html` still works without out/ dir
- **SPA routing**: `app.mount("/_next", ...)` for assets only + `@app.get("/{full_path:path}")` catch-all for SPA routing. Never use `app.mount("/", StaticFiles(html=True))` — it shadows all explicit routes

### File Structure
```
frontend/
├── app/
│   ├── ai-log/page.tsx
│   ├── dashboard/page.tsx
│   ├── keywords/page.tsx
│   ├── listings/page.tsx
│   ├── settings/page.tsx
│   ├── sites/page.tsx
│   ├── globals.css (Tailwind v4 @theme, cyberpunk palette, btn-ghost, btn-danger)
│   ├── layout.tsx
│   ├── page.tsx (redirects to /dashboard)
│   └── providers.tsx ('use client' QueryClientProvider)
├── components/
│   ├── ai-log/ (AILogCard, AILogFeed)
│   ├── dashboard/ (StatsGrid, ActivityLog)
│   ├── keywords/ (KeywordRow, KeywordsTable — dnd-kit, ScoringRulesPanel)
│   ├── layout/ (Header, Nav, StatusBar)
│   ├── listings/ (ListingRow, ListingsTable, ListingDetailPanel, ImageGallery)
│   └── sites/ (SiteRow, SitesTable — dnd-kit)
├── hooks/ (useSSE, useListings, useCountdown, useKeywords, useSites)
├── lib/
│   ├── api/ (listings, keywords, sites, config, engine, system, ai, scoring-rules)
│   ├── types.ts
│   └── utils.ts (cn, formatUptime)
├── store/ (engineStore, settingsStore)
├── out/ (static build — served by FastAPI)
└── vitest.config.ts (has @/ alias resolution)
```

### Tests (21 passing)
- theme.test.ts (2), types.test.ts (4), engineStore.test.ts (3)
- listings-api.test.ts (3), useCountdown.test.ts (2)
- StatsGrid.test.tsx (1), StatusBar.test.tsx (3), ListingRow.test.tsx (3)

---

## Dev Paths & Commands

### Node.js
- **Default installer** (`C:\Program Files\nodejs\`): usually on system PATH — open new terminal and run `node --version`; no manual PATH needed.
- **Portable** (if used): set PATH in each shell before `npx`/`npm`:
  - PowerShell: `$env:PATH = "C:\Users\Abbas\AppData\Local\nodejs-portable\node-v22.14.0-win-x64;$env:PATH"`
  - Bash: `export PATH="/c/Users/Abbas/AppData/Local/nodejs-portable/node-v22.14.0-win-x64:$PATH"`

### Common Commands
```bash
# Start backend
python worker.py                          # port 8000

# Frontend build (from project root)
export PATH="..." && npm run build --prefix frontend

# Run tests (from project root)
export PATH="..." && cd frontend && npx vitest run

# Combined build + test
export PATH="..." && cd frontend && npx vitest run && npm run build
```

---

## Key Gotchas (Never Forget These)

### Python / Backend
- **`threading.Event` not `asyncio.Event`** for cross-thread signalling — asyncio.Event is not thread-safe
- **SQLite `enabled` field**: Store as `int` (1/0), never `bool` — `filter(enabled==1)` breaks on Python `True`
- **JS in f-strings**: Use backtick template literals, never apostrophes — breaks Python f-string parsing
- **`db.flush()` before `db.commit()`**: SQLite WAL locking requires this
- **`calculate_attribute_score()` opens own DB session** — don't call inside an already-open session loop
- **`closing_alerts_sent`**: JSON list — always `json.loads` + append, never overwrite

### FastAPI Routing
- **NEVER `app.mount("/", StaticFiles(html=True))`** with explicit routes you want to preserve — the root mount captures ALL paths. SPA fallback returns 200+index.html for unknown paths, shadowing `@app.get("/legacy")` etc.
- **Fix**: `app.mount("/_next", ...)` for assets + `@app.get("/{full_path:path}")` explicit catch-all

### API Format
- **`/api/config` GET** returns flat `{key: value}` dict, NOT `Config[]` array
- **`/api/config` POST** expects flat dict — not `[{key, value}]` array
- Frontend `fetchConfig()` must call `.then(data => setConfig(data))` directly — no `.map()`

### Frontend
- **SSE disabled in static export** — `EventSource('/api/stream')` always 404s. Use HTTP polling instead
- **`createPortal` needs SSR guard** — `document.body` unavailable during Next.js SSR. Use `useEffect(() => setMounted(true), [])` before rendering portal

### HiBid
- **Apollo cache**: Search results pages have 0 Lot entries — images only from detail pages
- **Image dedup**: Use full URL including query string (HiBid CDN uses same path `img.axd` for all images, differentiated only by query params)

---

## Pending Work (Priority Order)

1. **Redis Cache Layer** — replace in-memory Python dicts; survives restarts; pub/sub for live dashboard
2. **Docker Compose** — one-command startup with PostgreSQL + Redis
3. **Lot Description Extraction** — AI only sees title; detail page descriptions → better AI-first accuracy. Needs `JS_DETAIL_TEXT` + `description` column on Listing + pass to `_ai_analyze()`


