tests/test_alembic.py points Alembic at an in-process SQLite database
in --sql mode so the migration files are validated end to end without
needing the real Postgres compose service. Asserts the documents,
chunks, and processing_events tables plus the unique constraints
appear in the generated DDL, and that the revision graph stays
linear at 0001_initial.
tests/test_routes_search.py monkeypatches
app.indexing.hybrid_search.run_search so the FastAPI route can be
exercised with the real SearchRequest/SearchResponse schemas. Covers
the happy path (rank, citation, reranked flag) and that empty queries
are rejected at schema validation before the backend is called.
pytest tests/test_alembic.py tests/test_routes_search.py -q: 4 passed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Production overlay narrows the dev defaults:
- removes published ports from postgres, minio, opensearch, qdrant,
redis - only the api container stays externally reachable;
- enables the OpenSearch security plugin and requires
OPENSEARCH_ADMIN_PASSWORD via ?:required interpolation;
- requires Qdrant API key, MinIO root credentials, postgres password,
and CORS_ALLOWED_ORIGINS to be set (no localhost fallback);
- doubles OpenSearch heap (-Xms2g -Xmx2g) and worker concurrency to 4;
- drops the MinIO management console.
Validated with:
set -a; . .env.prod.example; CORS_ALLOWED_ORIGINS=https://example.com
docker compose -f docker-compose.yml -f docker-compose.prod.yml config
The RUNBOOK was updated in the initial commit and already documents
the overlay invocation and credential rotation workflow.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The artifact-upsert helper was duplicated four times (scanner.py,
table_processor.py, figure_processor.py, pipeline.py) with slightly
different signatures. Consolidates into a single keyword-only function
keyed on (document_id, storage_key) - the identity the schema already
enforces - so re-running the pipeline never creates duplicate rows.
scanner / table_processor / figure_processor now import the shared
helper directly. pipeline.py keeps a thin local wrapper to preserve
the positional call sites at three artifact upsert points (OCR_PDF,
MARKDOWN, DOCLING_JSON).
Tests: 24 passed (5 health + 19 original).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CORS:
- New setting CORS_ALLOWED_ORIGINS (comma separated). Defaults cover
the three local Vite ports (5173, 5273, 4173); production overlay
expects the real origin in .env.prod.
- main.py wires CORSMiddleware from settings.cors_origins. No * in
production - see RUNBOOK and .env.prod.example.
- docker-compose.yml forwards the variable to both api and worker.
Tests:
- tests/test_api_health.py uses FastAPI TestClient and monkeypatches
the five probe functions (postgres/minio/opensearch/qdrant/redis).
Verifies the all-ok, any-error, and degraded paths, that the root
endpoint reports the configured api prefix, and that the CORS
preflight echoes the allowed origin.
- pytest tests/test_api_health.py -q: 5 passed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- vite-env.d.ts now declares ImportMetaEnv with the three VITE_*
variables the project uses, restoring proper typing for
import.meta.env in apiClient.ts.
- QualityFlag.tsx widens its 'flags' prop to accept the domain
QualityFlags type, the loose Record form used in mocks, or null,
ending the structural-mismatch errors at five callsites
(DocumentsPage, DocumentViewerPage, QualityControlPage,
ChunkPreview, SearchResultCard).
- DashboardPage trend callbacks are typed against DashboardStats so
the implicit-any complaints disappear without weakening intent.
npx tsc --noEmit -> clean. vite build -> ok.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds two-job CI (backend + frontend) running ruff, pytest (unit only -
skipping heavy ML deps), docker compose config validation for both dev
and prod overlays, plus npm ci -> eslint -> tsc -> vite build for the
frontend.
ESLint config uses the v9 flat-config format that the project was
already on (eslint v9 dropped .eslintrc support); replaces the broken
'eslint . --ext' invocation and adds @typescript-eslint, react-hooks,
and react-refresh plugins.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>