# CLAUDE.md

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

## Purpose

`ar-fingerprint-filter` is triggered by Kafka CDC events from the `artRelations`((hence `ar_`) MySQL database. It filters and normalizes those events, then kicks off the fingerprinter Step Function (`SFN_FINGERPRINTER_ARN`) for each relevant track change. It acts as a smart gate: skipping noise (update-only metadata changes to `lastModifiedBy`/`lastModifiedAt`) and deduplicating SFN executions via idempotent execution names.

## Development Commands

Run from this directory:

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

# With CI-style 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_handler" docker compose up --build lint-and-test

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

# Quick handler invocation without Docker
python simulate_event.py

# Send a sample event file to a running local function
./tests/sample_events/call.sh tests/sample_events/mysql/track_master_rights/created.json
```

Linting runs `yamllint`, `mypy` (entry: `src/app.py`, `tests/unit/`), and `flake8` (max line 120, single quotes, Google import order).

## Architecture

**Event flow:**
1. `decode_records` — decodes base64-encoded MSK Kafka records into `(source_key, payload)` tuples. Source key format: `aws:kafka:<topic-name>`.
2. `filter_events` — drops `update` ops where only `lastModifiedBy`/`lastModifiedAt` changed (via recursive diff in `_are_different`). All creates, deletes, and meaningful updates pass through.
3. `generate_outputs` — maps each `(source, raw_event)` to a typed event class (`TrackAudioOrRightsAttributes` or `TrackMasterRights`) which produces `OutputEvent` objects.
4. `throttle` — blocks until the running SFN execution count is below `SFN_FINGERPRINTER_MAX_RUNNING` (default 500) before starting new ones.
5. `execute_sfn` — starts one SFN execution per output event; silently skips `ExecutionAlreadyExists` errors (idempotency via `exc_id`).

**Event class hierarchy (`src/events/`):**
- `InputEvent` (base) → `BaseEvent` (MySQL CDC) → `TrackAudioOrRightsAttributes` / `TrackMasterRights`
- `OutputEvent` — carries `data_type`, `data_id`, `action`, `unique_id`, `timestamp`; serializes to JSON for SFN input; generates `exc_id` as `{data_type}-{data_id}-{action}-{unique_id}` (used as SFN execution name for dedup)

**Supported Kafka topics:**
- `cdc.artRelations.trackAudioAttributes` → `TrackAudioOrRightsAttributes` (create/delete only; updates ignored)
- `cdc.artRelations.trackRightsAttributes` → `TrackAudioOrRightsAttributes` (same behavior)
- `cdc.artRelations.trackMasterRights` → `TrackMasterRights` (create/update/delete all pass through)

## Key Config

| Env var | Default | Notes |
|---|---|---|
| `SFN_FINGERPRINTER_ARN` | required | ARN of the downstream fingerprinter state machine |
| `SFN_FINGERPRINTER_MAX_RUNNING` | `500` | Throttle ceiling for concurrent SFN executions |
| `ENVIRONMENT` | `dev` | Controls secrets resolution (`dev` skips LambdaSecretsManager) |
| `SENTRY_DSN` | optional | Can be set in `.env` (copy `.env.shadow`) or fetched from secrets manager |
