39246-vm/docs/MEMORY.md
2026-03-20 02:38:40 +03:00

9.0 KiB

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 7000
  • 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:7000 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

# Start backend
python worker.py                          # port 7000

# 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 boolfilter(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
  • Keyword batching retry warnings: the dashboard pins only retry candidates with attempt_count > 0 via /api/scrape/progress (queued-but-never-attempted items stay hidden until first failure).
  • Per-site visible override precedence: show_browser=true forces visible mode for all sites and ignores per-site custom_visible_browser; show_browser=false applies per-site visibility.
  • Retry-tracking state model: scrape_round_items moves through pending | in_progress | done | failed; each active round has a 4-hour retry window, and /api/scrape/progress computes hourly warn_due from last_hour_warn_at fallback logic (first_pending_at, then round start).
  • 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 exportEventSource('/api/stream') always 404s. Use HTTP polling instead
  • createPortal needs SSR guarddocument.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)

Redis Layer (added Session 32)

  • Enabled via REDIS_URL env var — app runs unchanged without it (graceful fallback)
  • _redis_publish("new_listing", {...}) fires on every alert; channel: ghostnode:events
  • _redis_set_stats(_stats) — call after every meaningful _stats write (cycle end, pause, resume, running)
  • GET /api/redis/status — connectivity check + cached stats hash
  • Install locally: pip install redis (auto-installed in Docker)

N18 — Lot Description (added Session 32)

  • JS_DETAIL_TEXT runs on the already-open detail page (same visit as images — zero extra HTTP cost)
  • description column: Text, max 1500 chars, nullable — in Listing ORM + _migrate_schema() for both SQLite and PostgreSQL
  • _build_ai_prompt(title, ai_target, description="") — description appended as Lot description: block when non-empty
  • _ai_analyze(title, ai_target, description="") — forwards to prompt builder
  • Re-analysis: after detail fetch, ai_match=1 listings with ai_target are re-evaluated with description; verdict updated in DB

Docker (added Session 32)

  • Dockerfile — Python 3.11-slim + Playwright Chromium; serves port 7000
  • docker-compose.yml — ghostnode + postgres:16-alpine + redis:7-alpine; health checks before app starts
  • DATABASE_URL + REDIS_URL wired in compose env
  • shm_size: 512mb on ghostnode service — Chromium requires shared memory

Pending Work (Priority Order)

  1. Docker Testing — run docker compose up --build; verify PostgreSQL migration, Redis pub/sub, Playwright headless in container
  2. Frontend: show description — add description field to ListingDetailPanel (already in to_dict(), just needs UI)
  3. vLLM production inference — replace Groq free tier for production scale