Files
transcription/.github/instructions/services.instructions.md
T
2026-06-28 12:33:57 -05:00

4.2 KiB

description, applyTo
description applyTo
Follow these guidelines when editing the services src/transcription/services/*.py

Services

Structure

  • Project core data models defined in models
  • 1 service class per data model
  • Only services directly interact with the database, and only through async methods
  • Services are completely independent of one another. Any operation that needs to use more than a single service, which is most of them, needs to have a separate orchestration function.

Error Handling

  • Service-specific errors defined at the top of the respective module and inherit from AppError
  • Use a context manager for large try/except blocks like in transcription

Checklist

  • Uses ServiceBase for common logic
  • CRUD methods created at the top
  • Session kwarg for AsyncSession to pass in a session object to each method
  • Services use self._session_scope in their methods to pass the session thru.
    • Multiple operations on the same object(s) require sharing a session between all the methods used.

CRUD Methods

  • Create, read, update, and delete, created in that order
  • Name format <operation>_<model >, for example create_document or update_job
  • All services must define these 4 methods first, and in that order

Transaction Finalization

When a service method accepts an optional session kwarg, write methods must use self._finalize to finalize the transaction properly according to whether or not they are sharing a session.

  • If session is None: the method owns the transaction and should commit().
  • If session is provided: the method must not commit; it should flush() so IDs and FK values are available to the caller's transaction.
  • Use refresh() on returned ORM objects when the caller needs DB-populated values (defaults, triggers, merged state).

Recommended helper behavior:

  • Inputs: active session object, original session arg (or a boolean ownership flag), and an optional list of objects to refresh.
  • Logic: commit when service-owned session, flush when caller-owned session, then refresh requested objects.

This keeps orchestration functions atomic: they can pass one shared session across multiple services and commit exactly once at the workflow boundary.

Workflow Transaction Boundaries

For multi-step job lifecycles (for example queued transcription jobs), orchestration functions must use explicit transaction phases.

Required boundary model:

  • Transaction A (claim): transition JobStatus.QUEUED -> JobStatus.PROCESSING and commit immediately.
  • Perform provider/network work outside database transactions.
  • Transaction B (terminal success): write transcript content and set JobStatus.TRANSCRIBED in the same shared-session commit.
  • Transaction B (terminal failure): write transcript error detail and set JobStatus.FAILED in the same shared-session commit.
  • Transaction C (retry path): write transcript error detail, increment retry count, and set JobStatus.QUEUED in one shared-session commit.

Atomicity rules:

  • Never commit transcript updates separately from the paired terminal/retry job status change.
  • Terminal state (TRANSCRIBED or FAILED) and transcript row changes must succeed or roll back together.
  • Retry persistence (QUEUED + retry increment + error detail) must succeed or roll back together.

Separation of concerns:

  • Worker modules should stay lightweight and delegate lifecycle transitions to service/workflow orchestration functions.
  • In workflows.py, process_queued_job should own one complete attempt lifecycle: QUEUED -> PROCESSING -> TRANSCRIBED|FAILED.
  • In workflows.py, advance_job should coordinate broader status progression around attempts (for example retry scheduling from FAILED -> QUEUED).
  • Services should expose session-aware write helpers (flush on caller-owned session) so orchestration controls commit boundaries.
  • Backoff/sleep behavior must run outside transactional scopes.

Service Composition

Some operations, like uploading a picutre, require modifications to multiple tables, which can be done by composing methods from the service object into a separate function.