Skip to content

Latest commit

 

History

History
117 lines (93 loc) · 7.49 KB

AGENTS.md

File metadata and controls

117 lines (93 loc) · 7.49 KB

AGENTS.md

Repository Layout

  • papyrus/api/routes: FastAPI routers and HTTP-facing request handling.
  • papyrus/services: service-layer business logic. Prefer new domain modules here instead of growing route handlers.
  • papyrus/schemas: Pydantic request and response models.
  • papyrus/models: SQLAlchemy models and metadata exports used by Alembic.
  • papyrus/config.py: validated environment configuration.
  • papyrus/core: database, exceptions, security and shared infrastructure.
  • alembic: Alembic environment and migration revisions.
  • tests/api/routes: endpoint behavior and contract tests.
  • tests/services: service-layer tests.
  • tests/conftest.py: shared fixtures and test database setup.

Coding Conventions

When working in the Papyrus workspace, read ../CODING_STYLE.md for the maintainer's reviewed examples. The rules below also apply in standalone checkouts.

  • Use meaningful local and callback names; avoid single-letter operands and redundant qualifiers when a clear role name is available.
  • Prefer clear branches or named predicates to nested conditional expressions and substantial inline validation conditions. Preserve predicate polarity and null handling when extracting helpers.
  • Extract meaningful constants and focused helpers for distracting literals or construction details. Preserve types, scope, return values, and behavior.
  • Keep Ruff's 120-character line length. Use trailing commas for deliberate multiline argument layouts, and give multiline model/schema fields breathing room while keeping related simple fields together.
  • Remove documentation that merely repeats a declaration and decorative section banners. Retain substantive contracts and constraints in docstrings.
  • Keep route handlers thin. They should parse input, enforce dependencies/auth, call a service, translate errors, and return schemas.
  • Put business rules, query orchestration, and transaction-aware logic in papyrus/services, not in route modules.
  • Reuse or extend existing schema modules before creating new top-level packages.
  • Follow existing async patterns with AsyncSession, explicit return types, and Pydantic schemas.
  • Keep consecutive single-line statements together. A change in purpose does not require a blank line, including query execution, result extraction, assignments, persistence calls, and returns.
  • Treat any multiline statement or block as a visual boundary inside a function. If one adjacent statement is multiline and both sides are real code, separate the two statements with a blank line.
  • Apply that rule to multiline conditionals, loops, with blocks, try blocks, multiline calls, multiline literals, and multiline return values. Treat if / elif / else as one block.
  • Do not add a blank line just because a docstring appears above the first statement in a function. The multiline-block rule applies between code statements, not between a docstring and the first line of code.
  • Do not pad the start or end of a block. Spacing follows blocks and multiline statements, not inferred logical phases or side effects.
  • When adding a new router module, register it in papyrus/api/routes/__init__.py.
  • Avoid adding dependencies unless the user explicitly asks for them.
  • Keep changes scoped. Do not refactor unrelated areas as part of a focused fix.
  • Do not leave inline comments. Sufficiently complex behavior that requires explanation should be documented using docstrings.

Runbook

  • Install dependencies: uv sync --extra dev
  • Start Postgres: docker compose up -d database
  • Stop services: docker compose down
  • Run the API locally: uv run uvicorn papyrus.main:app --reload
  • Apply migrations: uv run alembic upgrade head
  • Create a migration: uv run alembic revision --autogenerate -m "<message>"
  • Run all tests: uv run pytest
  • Run integration tests: uv run pytest -m integration
  • Run one test module: uv run pytest tests/api/routes/test_<module>.py
  • Lint: uv run ruff check .
  • Format: uv run ruff format .
  • Typecheck (installed, configured, and used in CI): uv run --locked mypy .

Testing Rules

  • Add or update tests for every behavior change.
  • Prefer route tests for HTTP contract changes and service tests for business logic.
  • If database behavior changes, add the narrowest regression test that proves the query or transaction behavior.
  • Mark schema-changing tests with pytest.mark.migration. Ordinary tests share the schema and truncate rows after each case; migration tests rebuild it before and after each case, including failures.
  • Use public_client or public_debug_client for routes that do not access the database. These fixtures reject accidental database access.
  • Set PAPYRUS_TEST_TIMING=/tmp/pytest-timing.json to export setup, call, and teardown timings. CI publishes these as a job summary and artifact.
  • Do not claim success without running the most relevant checks, or explicitly stating why they were not run.

Migration Rules

  • Add an Alembic revision for every schema change, constraint change, index change, or persisted-data backfill.
  • Update SQLAlchemy models first, then review autogenerated Alembic output before committing it.
  • Make sure new or changed models are imported from papyrus.models so Alembic metadata can see them.
  • Keep migrations small and pair them with the application code that depends on them.
  • Do not ship destructive or irreversible migrations without explicit user approval.

Completion Criteria

  • Relevant tests pass, or any unrun checks are called out with the reason.
  • Ruff passes on the changed scope.
  • Typechecking is run on the changed scope using the repo's configured Mypy setup.
  • Schema changes include a migration and verification.
  • New backend behavior follows thin-route and service-layer separation.
  • No new dependencies were added unless explicitly requested.

Auth Testing

Local auth testing supports Mailpit for SMTP capture, a dev auth sandbox at /__dev/auth-sandbox, and opt-in provider smoke tests.

Use .env.example, tests/api/routes/test_auth.py, and tests/integration/test_auth_smoke.py for current configuration and test entry points. See README.md for current auth/sync routes, local sandbox entry points and test lanes.

For client/server contracts, use the workspace's papyrus-sync-contract skill and ../.agents/skills/papyrus-sync-contract/references/CONTRACT_MAP.md when developing inside the Papyrus workspace.

To build the dev sandbox assets without the Vite dev server:

npm --prefix frontend/dev-pages run build

Workspace tooling

When checked out inside the Papyrus workspace, run from its root:

  • tools/papyrus deps server installs dev tools with uv sync --locked --extra dev.
  • tools/papyrus check server runs Ruff lint, non-mutating format checks, and Mypy.
  • tools/papyrus test server -- tests/services/test_sync.py runs focused tests.

The shared fixtures truncate test rows and rebuild tables for migration tests. Most route tests need PostgreSQL without being marked integration. Use a separate local test database, never the application database; do not run suites concurrently on the same database. The CLI checks the database target and holds a workspace test lock. External auth_smoke tests are excluded by default and require explicit provider configuration.

Sync uploads in papyrus/services/sync.py lock per owner, commit mixed batches atomically, and delete physical media after commit. library_sync.py enforces ownership and tombstones so delayed offline writes cannot resurrect deleted rows. Preserve those semantics when changing persistence or upload validation.