# CLAUDE.md

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

## Purpose

This Lambda migrates rights data from the rights registry (dynamoDB <env>-masters-active) into Fingerprint Rules. It consumes Kafka events from the `event.orchardSoundRecording.fingerprintRules` topic and translates carveout data into territory-level fingerprint rules via the OWS Sound Recordings API.

## Development Commands

Run from this directory (`lambda/sr-fingerprinting-rules-migrator/`):

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

# Run with exit codes
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_vendor_no_carveouts" docker compose up --build lint-and-test

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

# Invoke locally with a sample event
curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d @tests/sample_events/kafka/tuid_carveouts_no_rules.json

# Send a sample event using the helper script
./tests/sample_events/call.sh tests/sample_events/kafka/tuid_carveouts_no_rules.json
```

## Architecture

**Trigger:** AWS MSK (Kafka) — topic `event.orchardSoundRecording.fingerprintRules`

**Data flow:**

1. `handler()` in `src/app.py` receives a batch of Kafka records from MSK
2. `_decode_records()` base64-decodes each record's `value`, then parses the JSON payload. The payload `KEY` field identifies the object type (`vendor_id:`, `subaccount_id:`, or `tuid:`) and ID
3. Events are grouped by `obj_type` (`vendors`, `subaccounts`, `tracks`) and processed in separate batches
4. `_transform_event_to_ows_sound_recordings_call()` applies the carveout-to-rule logic (see below)
5. `src/connectors/ows_sound_recordings.py` calls `POST /{obj_type}/bulk/rules` on the OWS Sound Recordings API, passing all territory-level rules for each object

**Carveout-to-rule translation logic (critical business logic in `src/app.py`):**

The Kafka payload uses a "carveout" model (which territories are excluded), while the OWS API uses explicit territory lists. The transformation differs by object type:

- **Vendors:**
  - `star_carveout=true` → send empty territories with `monetize` policy (deletes vendor rules)
  - No carveouts → send `ALL_TERRITORIES` with `monetize` policy
  - Partial carveouts → send two rules: (1) inverse of carveouts with `monetize` policy, (2) carveout territories with `carveout` policy

- **Subaccounts / Tracks:**
  - `star_carveout=true` → send `ALL_TERRITORIES` with `carveout` policy
  - Otherwise → send territory carveout list directly with `carveout` policy

**`ALL_TERRITORIES`** is defined inline in `src/app.py` as a hardcoded list of all ISO 3166-1 alpha-2 territory codes (~249 entries). This is intentionally separate from the common module's territory list.

**OWS API connector** (`src/connectors/ows_sound_recordings.py`): Wraps the common `ows_sound_recordings.call()` with admin identity headers (`ADMIN_IDENTITY_ID`, `ADMIN_PROFILE_ID`, `ADMIN_PROFILE_TYPE` from `config.py`). Passes `services` as a query param (controlled by `SUPPORTED_SERVICES` config, defaulting to `['meta', 'tiktok']`). Retries on 408, 502, 503, 504.

**Error handling:** Decode failures (e.g., unknown KEY prefix) raise and halt the batch. OWS call failures are caught, logged, and returned as error dicts — the handler continues processing other object type batches.

## Configuration

`SUPPORTED_SERVICES` env var (comma-separated) controls which platforms receive rule updates. Defaults to `meta,tiktok`. Override via `.env` for local dev.

## Test Structure

- `tests/unit/test_handler.py` — tests for the full handler using hardcoded base64-encoded Kafka payloads (many test cases are commented out with their equivalent `assert_called_once_with` verifications)
- `tests/unit/connectors/test_ows_sound_recordings.py` — tests for the local OWS connector wrapper
- `tests/unit/common/test_ows_sound_recordings_base.py` — tests for the common OWS connector
- `tests/integration/` — skipped in CI; invokes the deployed Lambda via boto3
- `tests/sample_events/kafka/` — real Kafka event JSON files (with a `_value_decoded_example_info_only` field for human readability)

Note: Unit test fixtures use real base64-encoded Kafka message payloads rather than constructing them programmatically. When writing new handler tests, construct the base64 value by encoding a JSON object with `schema` and `payload` keys where `payload.KEY` is `<type>:<id>` and `payload.VALUE` is a JSON-encoded string of the rules array.
