Implement Phase 0b base project against the frozen test contract

All seven services (gateway, ledger, fraud, sanctions, insights, notifier,
audit), db/schema.sql plus optional_indexes.sql, scripts/seed.py and
smoke.py, the gateway-served frontend, and docker-compose.yml. Plain-text
stdlib logging and a weak manual X-Request-Id only -- no observability yet,
by design, per test_no_observability.py. Implements all 13 named chaos-flag
pathologies from docs/PATHOLOGIES.md behind Redis-backed runtime flags.

make smoke and make test-contract pass; make test-baseline and
make test-pathology pass except for a handful of load-sensitive tests
documented in tests/README.md rather than papered over.
This commit is contained in:
Movses Aslanyan
2026-09-05 08:12:30 +03:30
parent ccf1783779
commit 91afea92a1
51 changed files with 2757 additions and 0 deletions
+80
View File
@@ -0,0 +1,80 @@
-- Ledgerline schema. Idempotent: safe to run against an existing database.
-- Index policy is deliberate -- see docs/phase-0-base-project-spec.md §5.
-- Do NOT add transactions(account_id, created_at) or transactions(category_id)
-- here; those live in db/optional_indexes.sql, applied only by `make fix-index`.
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
CREATE TABLE IF NOT EXISTS accounts (
id UUID PRIMARY KEY,
holder_name TEXT NOT NULL,
currency CHAR(3) NOT NULL,
balance_minor BIGINT NOT NULL,
is_hot BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE IF NOT EXISTS merchant_categories (
id SMALLINT PRIMARY KEY,
code TEXT NOT NULL UNIQUE,
label TEXT NOT NULL,
risk_weight NUMERIC(3,2) NOT NULL
);
CREATE TABLE IF NOT EXISTS transactions (
id UUID PRIMARY KEY,
account_id UUID NOT NULL,
kind TEXT NOT NULL,
amount_minor BIGINT NOT NULL,
currency CHAR(3),
merchant_name TEXT,
category_id SMALLINT,
status TEXT NOT NULL,
risk_score NUMERIC(4,3),
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- Parallel workers otherwise let a plain sequential scan of this table
-- compete with an index scan on raw CPU parallelism for higher-selectivity
-- accounts (e.g. the 5 hot ones, each ~6-8% of all rows) -- masking exactly
-- the missing-index pathology this table's index policy (README §5) exists
-- to demonstrate. Scoped to this one table, not a database-wide setting.
ALTER TABLE transactions SET (parallel_workers = 0);
CREATE TABLE IF NOT EXISTS sanctions_entries (
id SERIAL PRIMARY KEY,
normalized_name TEXT NOT NULL,
country CHAR(2),
list_source TEXT,
added_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE IF NOT EXISTS notifications (
id UUID PRIMARY KEY,
transaction_id UUID NOT NULL,
channel TEXT NOT NULL,
status TEXT NOT NULL,
attempts INT NOT NULL DEFAULT 0,
sent_at TIMESTAMPTZ
);
CREATE TABLE IF NOT EXISTS audit_events (
id BIGSERIAL PRIMARY KEY,
transaction_id UUID NOT NULL,
event_type TEXT NOT NULL,
payload JSONB,
recorded_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- Not part of the base spec's domain model: a single-row table the seed
-- script writes its summary manifest into, so both `scripts/smoke.py` and
-- the test suite can read back what a given seed run actually produced
-- (row counts, hot/sanctioned/poison account ids, the SEED value used)
-- without re-deriving it via ad-hoc queries.
CREATE TABLE IF NOT EXISTS seed_manifest (
id SMALLINT PRIMARY KEY DEFAULT 1,
data JSONB NOT NULL
);
CREATE INDEX IF NOT EXISTS accounts_holder_name_idx ON accounts (holder_name);
CREATE INDEX IF NOT EXISTS sanctions_entries_normalized_name_idx ON sanctions_entries (normalized_name);