# CONVENTIONS — document-scanner

_Last updated: 2026-05-21_

## Summary

The codebase follows standard Python and Vue 3 conventions without heavy tooling enforcement. Backend uses async/await throughout with type hints on public interfaces. Frontend uses Vue Options API with Pinia stores as the data layer. No linter or formatter configuration is committed.

---

## Python Conventions (Backend)

### Naming
- Files: `snake_case.py`
- Classes: `PascalCase` (e.g., `AnthropicProvider`, `ClassificationResult`)
- Functions/variables: `snake_case`
- Constants: `UPPER_SNAKE_CASE` (e.g., `MAX_STORED_CHARS`, `DATA_DIR`)
- Private helpers: leading underscore (e.g., `_extract_pdf`, `_parse_classification`)

### Async
- All API endpoint functions are `async def`
- All `AIProvider` methods are `async def`
- `pytest-asyncio` with `asyncio_mode=auto` (set in `pytest.ini`)

### Type Hints
- Used on public function signatures in `ai/` layer and `services/`
- Dataclass used for `ClassificationResult` (`@dataclass` with `field(default_factory=...)`)
- Not used consistently in `api/` routers (rely on FastAPI/Pydantic implicit validation)

### Error Handling
- `extractor.py` wraps all extraction in `try/except Exception` and returns error strings (never raises)
- AI providers raise on hard failures; caller (`classifier.py`) is responsible for propagating
- No global exception handler registered in `main.py`

### Imports
- Standard library first, then third-party, then local — not enforced by isort
- Heavy library imports (`fitz`, `pytesseract`, `docx`) are deferred inside functions to avoid import-time cost when unused

### Module Docstrings
- Present on `extractor.py` and `test_classifier.py`; absent elsewhere

---

## JavaScript / Vue Conventions (Frontend)

### Naming
- Vue files: `PascalCase.vue` (e.g., `DocumentCard.vue`, `AppSidebar.vue`)
- Pinia stores: `camelCase` filename matching store ID (e.g., `documents.js` → `useDocumentsStore`)
- Views: `<Name>View.vue` suffix
- Components grouped by domain in subdirectories: `documents/`, `topics/`, `upload/`, `layout/`

### Vue Style
- Options API used throughout (not Composition API)
- Props defined with type and default; no `defineProps` (Options API syntax)
- `v-model`, `v-for`, `v-if` used directly in templates

### Pinia Pattern
- Each store encapsulates `state`, `getters`, and `actions`
- Actions call `src/api/client.js` — components never import `client.js` directly
- Stores are the single source of truth; views read from store state

### API Client
- `src/api/client.js` is the sole HTTP adapter
- All paths are prefixed `/api/` (proxied to backend in dev via Vite config)

### Styling
- Tailwind CSS utility classes used directly in templates
- No scoped `<style>` blocks observed in component list
- Global styles in `src/style.css`

---

## API Design Conventions (Backend)

- All endpoints prefixed `/api/` (set per router)
- JSON responses; multipart for file upload
- HTTP verbs follow REST: GET list, GET by ID, POST create, PUT/PATCH update, DELETE remove
- No versioning (`/api/v1/`) — flat namespace

---

## Configuration

- Runtime paths controlled entirely by `DATA_DIR` env var (defaults to `/app/data`)
- AI settings persisted in `data/settings.json` — no env var overrides at runtime for provider config (except `ANTHROPIC_API_KEY` / `OPENAI_API_KEY` noted in `.env.example`)
- No `.env` loading in backend code — env vars passed via Docker Compose `environment:` block

---

## Gaps / Unknowns

- No ESLint, Prettier, Black, or Ruff configuration committed
- No pre-commit hooks
- No consistent JSDoc or Python docstring coverage
