scholarr/agents.md
Justin Visser 804b98423c docs: rewrite agents.md with complete, accurate agent instructions
- Add file length target (400 lines) and ceiling (600 lines)
- Fix testing command (was missing python -m pytest)
- Add conventional commits, ruff/mypy, structured_log, uv rules
- Remove duplication with contributing.md
- Tighten UI section with concrete references

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 11:02:59 +01:00

3.8 KiB

AI Agent Instructions: Scholarr

Adhere strictly to these constraints when working on this codebase.

1. Code Quality

  • Function length: 50 lines max. Extract helpers ruthlessly.
  • File length: 400 lines target, 600 lines hard ceiling. Files above this must be decomposed before adding more code.
  • DRY: Abstract repeated logic immediately. No duplicate boilerplate for queries, responses, or error handling.
  • Negative space programming: Fail fast with explicit assertions and guard clauses. No silent failures, especially in DOM parsing.
  • Cyclomatic complexity: Flatten with early returns. No deep nesting. No magic numbers.
  • No dead code: Do not leave commented-out code, unused imports, or backward-compatibility shims. Delete cleanly.

2. Architecture

Data Model

  • Scholar tracking is user-scoped. Never assume global links between users and Scholar IDs.
  • Publications are global, deduplicated records. Deduplicate via cluster ID and normalized fingerprinting.
  • Read/unread, favorites, and visibility state live on the scholar-publication link, not the publication.

Service Boundaries

All business logic lives in app/services/<domain>/. No flat files in app/services/ root. Each domain owns its application service, types, and helpers.

API Envelope

All /api/v1 responses use this exact envelope:

Success: {"data": ..., "meta": {"request_id": "..."}}
Error:   {"error": {"code": "...", "message": "...", "details": ...}, "meta": {"request_id": "..."}}

Use the Pydantic envelope schemas in app/api/schemas.py. Do not construct raw dicts.

3. Scrape Safety (Immutable)

These constraints prevent IP bans. They are not tunable to zero and must not be optimized away.

  • Enforce INGESTION_MIN_REQUEST_DELAY_SECONDS (default 2s) between all external requests.
  • Default to direct ID or profile URL ingestion. Name searches trigger CAPTCHAs.
  • Respect INGESTION_SAFETY_COOLDOWN_BLOCKED_SECONDS (1800s) and INGESTION_SAFETY_COOLDOWN_NETWORK_SECONDS (900s) upon threshold breaches.

4. Logging

Use structured_log() from app/logging_utils.py for all domain logging. Do not use raw logger.info() / logger.warning() calls.

from app.logging_utils import structured_log

structured_log(logger, "info", "ingestion.run_started", user_id=user_id, scholar_count=count)

Every event name should be dot-namespaced to its domain (e.g., arxiv.cache_hit, ingestion.safety_cooldown_entered).

5. Stack & Tooling

  • Backend: Python 3.12+, FastAPI, SQLAlchemy 2.0 (async/asyncpg), Alembic
  • Frontend: TypeScript, Vue 3, Vite, Tailwind CSS
  • Infrastructure: Multi-stage Docker, Docker Compose
  • Package manager: uv (used in Dockerfile and CI; uv run prefix for all commands)
  • Linting: ruff check . and ruff format --check . (config in pyproject.toml)
  • Type checking: mypy app/
  • Versioning: python-semantic-release with conventional commits

6. Commits

Follow Conventional Commits:

<type>(<scope>): <description>

Types: feat, fix, docs, ci, refactor, test, chore, perf.

7. Testing

All tests run inside containers:

# Unit tests (default, excludes integration)
docker compose -f docker-compose.yml -f docker-compose.dev.yml run --rm app python -m pytest

# Integration tests
docker compose -f docker-compose.yml -f docker-compose.dev.yml run --rm app python -m pytest -m integration

Markers: integration, db, migrations, schema, smoke.

8. Frontend

  • Use the tokenized theme system (frontend/src/theme/presets/). Do not hardcode colors.
  • Integrate Tailwind with preset theme tokens. Reference frontend/scripts/check_theme_tokens.mjs for enforcement.
  • Every UI element must have a clear purpose. Clarity through styling and language.