# CLAUDE.md

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

## Repository Overview

Monorepo of ~20 AWS Lambda functions for managing and delivering sound recordings to platforms (TikTok, YouTube, etc.). Each Lambda lives in `lambda/<function-name>/` and is self-contained with its own Docker setup, dependencies, and tests. Shared utilities live in `common/`, which is symlinked into each Lambda as `lambda/<function-name>/src/common`.

## Development Commands

All development happens through Docker Compose **inside each Lambda directory**:

```bash
cd lambda/<function-name>

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

# With exit codes (for CI-like behavior)
docker compose up --exit-code-from lint-and-test --abort-on-container-exit --build lint-and-test

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

# Invoke locally
curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d @tests/sample_event.json

# Restart after code changes (no rebuild needed for .py files)
docker compose restart function

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

# Pass custom pytest args
TEST_ARGS="-k test_specific_name" docker compose up --build lint-and-test
```

**Linting** uses `flake8` (max line 120, single quotes, Google import order) + `yamllint`.
**Tests** run with `pytest tests/ --cov-report term --cov=src`.

Some lambdas also have a `simulate_event.py` at their root for quick local handler invocation without Docker:
```bash
python simulate_event.py
```

## Lambda Function Structure

Every Lambda follows this layout:
```
lambda/<function-name>/
├── src/
│   ├── app.py           # Lambda handler entry point
│   ├── connectors/      # External service integrations
│   └── common/          # Symlink → ../../common
├── tests/
│   ├── unit/            # Run in CI
│   ├── integration/     # Skipped in CI (require real AWS/external services)
│   └── sample_event.json
├── config.py            # Env config (AWS region, Sentry DSN, OWS creds, etc.)
├── docker-compose.yaml  # Mounts ~/.aws/credentials for local AWS access
├── Dockerfile           # Production image (Python 3.13)
├── Dockerfile.tests     # Test image
├── requirements.txt
└── requirements-test.txt
```

## Architecture Patterns

**Handler pattern:** `src/app.py` contains the Lambda handler, typically importing from `src/connectors/` and `src/common/`.

**Configuration:** `config.py` at the Lambda root reads environment variables. Secrets come via `LambdaSecretsManager` from `secrets_manager.lambda_ext` in QA/PROD; local dev uses plain env vars set in a `.env` file (see `.env.example` in each Lambda). Lambda-specific vars (e.g., `KAFKA_BROKERS`, `SNOWFLAKE_ACCOUNT`, S3 bucket names) are required and will raise `KeyError` if missing.

**Observability:** All functions emit to **Datadog** (via Lambda extension v86) and optionally to **Sentry** (set `SENTRY_DSN` in `.env`).

**HTTP client:** Functions use `owsrequest` (internal Sony library), either v2.10.1 or v3.0.0 depending on the function.

**Kafka:** Some functions are triggered from or publish to Kafka (e.g., `sr-fanout`, `sr-registry-to-kafka`).

**Snowflake:** ACK-processor functions integrate with Snowflake for tracking delivery state.

**GraphQL:** TikTok-related functions use `gql` for backend communication.

## Common Module (`common/`)

Shared utilities symlinked into every Lambda:
- `connectors/` — AWS (S3, SFN, Kafka), OWS API, SFTP, GraphQL, Snowflake
- `ack_processor/` — Delivery acknowledgment handling
- `constants/` — Territory lists, delivery configuration
- `exceptions/` — Custom exception classes
- Utility helpers for logging, decorators, and timing

Changes to `common/` affect all Lambdas — test broadly when modifying shared code.

## AWS Credentials for Local Development

The `docker-compose.yaml` in each Lambda mounts `~/.aws/credentials`. Use `awsume` to set the correct profile before running containers. The `AWS_PROFILE` environment variable in docker-compose controls which profile is used.

## Dependencies and Package Registry

All `requirements.txt` files use The Orchard's internal PyPI: `-i https://pypi.theorchard.io/pypi/`. You must have network access and credentials for this registry to install dependencies locally outside Docker.

`lambda/dependencies.txt` maps each Lambda to its external service dependencies (GraphQL Router, OWS APIs, Kafka topics, Snowflake, SFTP, SFNs) — useful as a reference when tracing integrations.

## CI/CD

Jenkins (not GitHub Actions) handles CI/CD via `Jenkinsfile` at the repo root. It uses a monorepo-aware shared library (`MonorepoUtils`) that detects which Lambdas changed and only builds/deploys those. Deployments target ECR (`086679231553.dkr.ecr.us-east-1.amazonaws.com`) and go to QA automatically on `master`; PROD requires the `DEPLOY_TO_PROD` flag. Each Lambda's `software-catalog.yaml` is published to the Datadog Software Catalog on PROD deploy.
