Files
transcription/AGENTS.md
T
Jim Lancaster e5ef4d4422
Quality Gate / gate (push) Successful in 2m41s
Update instructions, agents, skills - part 1
2026-09-02 14:05:41 -05:00

6.1 KiB

AGENTS.md

Orientation for AI agents working in this repository. This file is a router, not a spec: it points at canonical authority and flags the traps that are expensive to discover by trial. Where this file and docs/* disagree, docs/* wins.

What This Is

A document transcription system that preserves durable archival records (Documents, Sources, People) and executes page transcription asynchronously through vision/LLM providers. Its defining constraint is evidence: every machine attempt is recorded append-only with request/response provenance. Features that would lose, mutate, or obscure that history are wrong regardless of how convenient they are.

Stack: Python 3.12+ · FastAPI + NiceGUI · SQLModel/SQLAlchemy (SQLite-first, PostgreSQL- compatible) · Pydantic V2 · asyncio worker · OpenRouter adapter.

Commands

This is a uv project. Nothing is on PATHruff, ty, and pytest all require uv run. Bare invocations fail with command-not-found.

uv run ruff check .                  # lint (blocking in pre-commit)
uv run ruff format --check .         # format (blocking in pre-commit)
uv run ty check                      # types (blocking in pre-commit)
uv run pytest -q -m "not external"   # default verification run

external marks tests that hit live services; always exclude it unless explicitly asked. All four commands are expected to pass clean — there is no tolerated baseline of failures. If ty reports something, fix it or suppress it inline with a rationale comment; a bare ignore will not survive review.

Authority Order

Resolve every question in this order, and stop at the first that answers it:

  1. docs/* — canonical. Start at docs/index.md, which defines the reading order. docs/invariant/* holds cross-version rules that outlive any release.
  2. .github/instructions/*.md — active steering, auto-attached when you edit matching paths. Covers services, UI, error handling, and documentation sync.
  3. .github/skills/* — periodic audit procedures (code review, provenance, test effectiveness).
  4. tests/ — deterministic enforcement. A guard test is the ground truth for whatever rule it encodes.

docs/reviews/** is not canonical. Those are dated, opinionated snapshots that were accurate when written and may since have been fixed, superseded, or found wrong.

Layout

Path Role
src/transcription/ui/**, api/** Interface. No direct persistence access.
src/transcription/services/** Domain logic and transaction ownership.
src/transcription/db/** Models and persistence.
src/transcription/providers/** Provider adapters; provider details stop here.
src/transcription/worker.py Asyncio worker loop.
tests/ Includes boundary/contract guards, not just behavior tests.

Enforced Boundaries

These are not conventions — a test fails if you break them:

  • No service-to-service imports (test_service_boundaries.py). Compose in the caller.
  • No persistence access from pages/components (test_ui_boundaries.py, allowlist-based).
  • No hand-rolled error notifications in UI — use the shared error presenter.
  • No stringly-typed status literals — use the enums (test_model_contract_guards.py).
  • Attempt history is append-only (test_v42_evidence.py).
  • docs/schema.md stays field-accurate with db/models.py.
  • Orphans are tracked, not toleratedtest_orphan_sweep.py records each retained orphan with rationale in KNOWN_ORPHANS.

Traps

Non-obvious things that have already caused real bugs here:

  • AppError.message vs AppError.detail. message is user/API-facing and must stay generic — never put exception text or filesystem paths in it. detail is internal-only and is what reaches logs and ExecutionAttempt.error_detail. Putting root-cause data in message leaks; removing it from detail silently degrades provenance. See docs/error_handling.md.
  • Two competing atomicity invariants in services/workflows.py. Intermediate pages must commit individually (durability across a long multi-page job); the final page must commit atomically with the terminal job status. Collapsing the batch into one transaction satisfies the second and destroys the first. Both are guarded — test_workflows_reliability.py and tests/integration/test_pipeline_atomicity.py.
  • Shared symbols have more consumers than the obvious one. Before changing a model field, exception attribute, or helper return value, grep for every consumer including tests. Evidence and logging paths frequently read the same fields the UI does.
  • Tag is owned by two services, on purpose. Every other model has exactly one owning service, so the ownership rule reads as absolute — it isn't. Tag is a single table reached through two RegistryService[Tag] facades, TagRegistry (documents) and PersonTagRegistry (people), which count usage through DocumentTag and PersonTag respectively. Changing tag semantics through one facade silently changes the other. Consolidating them under one service is not a cleanup; it makes the other side a cross-aggregate writer.
  • Import style: ruff isort runs with force-single-line = true. One import per line.
  • Latent defects have ordering constraints. Some code is unreachable only because of a current setting or single-instance deployment. Fix it before the change that unblocks it, not after.

Change Protocol

  • Write the failing test first for behavioral fixes, and confirm it actually fails for the reason you think. Several bugs here were subtle enough that a test written afterward would have passed against the broken code.
  • Update docs in the same change when you alter a contract, behavior, or scope — see .github/instructions/documentation-sync.instructions.md.
  • Do not commit unless asked. Making a requested change is not consent to commit it.
  • Do not push or open PRs on your own initiative.
  • Scope discipline: fix what was asked plus what your change genuinely breaks. Pre-existing unrelated issues are a separate conversation.