Skip to content

Latest commit

 

History

History
149 lines (121 loc) · 5.97 KB

DEVELOPMENT.md

File metadata and controls

149 lines (121 loc) · 5.97 KB

Development Guide

Requirements

  • Python 3.12 (e.g. via uv)
  • Node.js 20+ and npm

No global Python packages are needed — the spaCy model is a pinned pip dependency.

Setup

# Backend
cd backend
python3.12 -m venv .venv            # or: uv venv --python 3.12 .venv
source .venv/bin/activate
pip install -e ".[dev]"             # or: uv pip install -e ".[dev]"

# Frontend
cd ../frontend
npm install                         # or: npm ci

Optional local embeddings for semantic validation:

cd backend && pip install -e ".[semantic]"

The model loads lazily on first use and is never downloaded at startup. Without it, suggestions still work and meaning checks report UNAVAILABLE.

Running

# Backend on :8000
cd backend && uvicorn app.main:app --reload --port 8000

# Frontend on :5173 (proxies /api to :8000)
cd frontend && npm run dev

Tests and checks

cd backend  && .venv/bin/pytest -q      # 675 tests, deterministic, no network
cd frontend && npm run typecheck        # tsc --noEmit
cd frontend && npm test                 # 195 tests across 13 files (Vitest + RTL)
cd frontend && npm run build            # typecheck + production build

All tests must be deterministic and offline:

  • Provider behaviour is mocked with httpx.MockTransport or replaced by the deterministic fake provider.
  • Semantic tests use FakeEmbeddingBackend. They never download or load a real model.
  • Frontend tests are jsdom only and never touch the network.
  • Tests read no developer .env: get_suggestion_service and get_semantic_validator are lru_cached, so tests override the FastAPI dependency or set environment variables explicitly. conftest.py disables semantic validation by default.

New metrics need golden tests with manually verifiable expected values. A failing test is fixed in the implementation, never weakened — unless the test itself is wrong, which should be rare and explained.

Code conventions

  • Production-quality, typed, modular code. Files stay focused; avoid inventing tiny abstractions for their own sake.
  • Validate all API inputs. Never return NaN or Infinity.
  • Compute each metric once and share the value — no duplicate logic.
  • Do not add dependencies without justification. There is deliberately no NumPy, no charting library and no LLM SDK.
  • Routes stay thin; logic belongs in a service.
  • All tunable constants live in app/config.py.

Frontend conventions

  • src/styles.css is the single source of design tokens. Never scatter hex values or one-off styles into components — reuse tokens and the shared classes (.btn, .badge, .notice, .kv-list, .status-line, .empty-state).
  • Light theme only.
  • Button hierarchy: .btn-primary for Analyze / Suggest alternatives / Apply, .btn-secondary, .btn-ghost, and .btn-outline-danger / .btn-danger for destructive or override actions. An override must never look ordinary.
  • Destructive and override actions use the ConfirmDialog primitive (Escape cancels, focus moves inside, Tab is trapped, backdrop click cancels).
  • Statuses are conveyed by text plus shape or icon, never colour alone.
  • Every user-facing string uses stable terminology and contains no sprint labels, internal enum names or debug identifiers.
  • EmptyState for empty states, spinners with role="status" for loading, .notice for informational copy. Never fake a progress percentage.
  • Responsive: side-by-side editor above 1120px, stacked below; the problem map stacks at 900px; mobile stack at 720px. Guard grids with min-width: 0 and use overflow-wrap to prevent horizontal overflow.
  • prefers-reduced-motion must keep working.
  • Frontend tests query by role or text, never by CSS class, and use no page snapshots.

Gotchas

These have each cost time at least once.

  • spaCy abbreviations — the sentencizer treats a single letter plus a period ("t.") as an abbreviation. Tests that need many sentences must use multi-letter words.
  • Emoji and astral characters tokenize as word tokens in spaCy and therefore count toward word counts.
  • Offsets are UTF-16 code units, matching JavaScript string indexing, not Python code points. Issue excerpt fields come verbatim from the original text and the frontend relies on those offsets for marks.
  • Editor offsets are valid only against the analysis snapshot. The UI marks the map stale after any edit and disables stale navigation until re-analysis. This is by design, not a bug to fix.
  • Suggestion freshness — the backend re-slices the supplied text at the issue's UTF-16 offsets and rejects mismatches as STALE_ANALYSIS (409).
  • .venv lives in backend/ and is never committed. The spaCy model is a pinned pip dependency rather than a post-install download.
  • Node and Python tooling may be installed user-locally in ~/.local/bin; make sure it is on PATH.
  • Readability uses textstat's internal tokenization while everything else uses spaCy. This is documented and intentional.
  • Semantic pipeline uses its own lazy spaCy pipeline with parser and NER disabled. The tagger needs tok2vec, so only parser and NER can be disabled.
  • Legacy profiles (no advanced key) derive only what their pooled fields support. Opening repetition is never derived.
  • Custom protected terms are literal and case-sensitive substring matches, not regexes or fuzzy matches.

Repository conventions

  • Do not modify unrelated files, and do not scan .venv, node_modules, dist, build, __pycache__ or .git.
  • Use targeted commands rather than large log dumps.

Project layout

backend/app/          FastAPI application, all local analysis and validation
backend/tests/        675 deterministic tests
frontend/src/api/     typed API client
frontend/src/editor/  editing workspace, undo/redo, diff, suggestion hooks
frontend/src/components/  one component per screen or analysis section
docs/                 this documentation set