# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Purpose

`sr-create` upserts an Orchard Sound Recording (OSR) via the OWS sound recordings API. It accepts two event shapes:

1. **Track event** (from a Step Function or similar): `{"label": "Track", "id": <track_id>}` — e.g. a `created` operation event from an OWS entity stream.
2. **ACR fingerprinter output**: `{"track_ids": ["123", ...]}` — a list of track IDs (as strings; cast to `int` before the API call).

It returns `{"sound_recording_ids": [...]}` — a sorted, deduplicated list of OSR UUIDs.

A 409 from OWS means the sound recording already exists; the connector swallows it and returns `None`, which is filtered out of the results. All other `HTTPError`s propagate and fail the Step Function execution.

## Development Commands

Run from this directory (`lambda/sr-create`):

```bash
# Run tests and linting
docker compose up --build lint-and-test

# Run with exit codes (CI-style)
docker compose up --exit-code-from lint-and-test --abort-on-container-exit --build lint-and-test

# Skip linting
SKIP_LINT=true docker compose up --build lint-and-test

# Run a specific test
TEST_ARGS="-k test_handler_input_track" docker compose up --build lint-and-test

# HTML coverage report (output in htmlcov/)
COV_REPORT=html docker compose up --build lint-and-test

# Start the function locally (port 9000)
docker compose up --build -d function

# Invoke locally
curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"label": "Track", "id": 48383573}'

# Quick local invocation without Docker
python simulate_event.py
```

## Key Design Details

**Connector layer** (`src/connectors/ows_sound_recordings.py`) is a thin wrapper around `src.common.connectors.ows_sound_recordings.call`. It passes `APPLICATION_NAME` and `ENVIRONMENT` from `config.py` as the caller identity. Retry codes for transient errors: 408, 502, 503, 504.

**Config** (`config.py`): `ENVIRONMENT` defaults to `dev`. In QA/PROD, secrets (e.g. `SENTRY_DSN`) are fetched via `LambdaSecretsManager`; locally, set them in a `.env` file (copy `.env.shadow` as a starting point).

**Sentry** is initialized at module load time using `config.secrets_manager_client.get_cred('SENTRY_DSN')`. Tests that reload `src.app` must mock `config.secrets_manager_client` to control this path.

**Integration tests** (`tests/integration/`) invoke the real `qa-lambda-sr-create` function via `boto3` and are not run in CI. The placeholder test intentionally fails — replace it with real assertions before use.
