Repository layout

.
├── src/                    application code; also the root of the Lambda artifact
├── tests/                  pytest suite (unit/ and integration/), AWS mocked with moto
├── scripts/                one-off operational scripts
├── docs/                   this Antora component
├── template.yaml           SAM template: RulesApi, API and worker functions, IAM, environment mapping
├── samconfig.toml          SAM config environments: dev and prod
├── Makefile                every developer, CI and SAM build command
├── pyproject.toml, uv.lock dependencies (uv) and tool configuration (ruff, complexipy, mypy, pytest, coverage)
└── .github/                CI and deploy workflows, shared setup action

Source packages

src/ is the import root: modules are imported by bare name (from settings import get_settings), both in Lambda and in tests. make check-names fails if an installed distribution shadows one of these names.

Package Responsibility

handlers/

Lambda entry points api (API Gateway REST to WSGI) and worker (dispatches Kinesis, SNS and schedule events), plus the watchdog and crm_event jobs the worker runs

web/

Flask app factory (create_app), flask-restx namespaces in web/api/, plain blueprints in web/views/

pipeline/

Stage, stream and action classes: stage_0_initial, stage_1_preprocess, stage_2_postprocess

services/

Rule engine, rule, template and history services, events service

domain/

Event and rule models, operators, selectors, resolvers, action registry and action fields

persistence/

DynamoDB access compatible with the existing tables; table schemas used by the moto tests

integrations/

AWS clients (SNS, Kinesis, Firehose, EventBridge), internal Payability APIs, Segment, search

settings.py

Environment settings (get_settings()) and the common/crm secret (get_secret())

observability.py

Logging and Sentry, configured once per cold start

Dependencies point inwards: handlers and web use services and pipeline, which use domain, persistence and integrations. domain doesn’t import from web or handlers.

Tests

Path Scope

tests/unit/

Models, operators, utils, pipeline building blocks, stage 1 parsing

tests/integration/

API namespaces, CRUD against moto DynamoDB, the rule engine with mocked HTTP, handlers, route layout

tests/conftest.py

Test environment (no AWS access, secrets from environment variables) and shared fixtures

tests/helpers.py, tests/fakes.py

moto resources, mocked authentication, sample rules and events

tests/ is a regular package (it has init.py) because pyb-flask-utils and the EventLogger SDK both install a top-level tests package into site-packages.