# Backend Agent Operating Contract ## Phase gate This repository is authorized for SDD Phase 4 implementation as of 2026-08-20. Follow `tasks/todo.md` in dependency order and keep the approved capability specs authoritative. New capabilities or changes outside those contracts still require specification and approval before implementation. For every Go-related task, always invoke and follow the `golang-how-to` skill, then the secondary Go skills it routes to. The capability-oriented modular-monolith architecture and manual-constructor dependency injection are approved; changing either requires a new ADR and user approval. ## Product invariants - Use the canonical domain terms in `CONTEXT.md`; update that glossary when a term's meaning is approved, without adding implementation details to it. - The backend is the authority for authentication, authorization, tenant isolation, metric definitions, and data provenance. - Every merchant-scoped query derives merchant identity from the authenticated server-side session; never trust a client-supplied merchant identifier as authorization. - Cross-merchant results are anonymized same-category aggregates only. Exclude the active merchant and suppress the benchmark unless at least 10 other merchants and 1,000 combined peer sessions contribute in the selected period. Use one value per peer merchant to calculate the cohort median and direction-aware performance percentile; never transaction-weight by default. - Treat `payer_card_key` as a pseudonymous card identifier whose equality is valid only inside one merchant. Retain it unchanged only for internal deterministic analytics; never expose it through merchant APIs, logs, exports, or AI inputs, and never describe it as a card number, cross-merchant identity, or verified person. - Use integer minor currency units or an explicitly documented fixed-precision representation; never floating point for money. - Interpret timezone-naive challenge timestamps as `Asia/Tehran`, preserve their source provenance, and normalize canonical database/API timestamps to UTC `timestamptz`/ISO 8601. Any change to this assumption requires a new import version and ADR. - Raw imports are immutable inputs; corrections are new import versions, not silent mutation. - CSV ingestion is a one-time bootstrap/recovery command only. Never add a file watcher, polling schedule, recurring CSV ETL, or ownership of ZarinPal's production writes without a new approved spec. - Treat source `adjusted_fee` as the merchant's real fee for this product. Normalize it once per session as `fee_amount_rial`; do not sum the repeated attempt-row value. - Treat both `Paid` and `Verified` as successful bank payments. `Paid` still requires merchant verification; `Verified` is finalized. Never classify `Paid` as failure. - A source record with `try_seq = 0` and `try_status = NoAttempt` creates a session with zero attempts. Never insert it into `payment_attempts` or count it as a failed/retried attempt. ## Go baseline after approval - Keep `main` packages thin under `cmd/`; business code lives under `internal/`. - Wire dependencies manually with explicit constructors at the composition root. Do not introduce a DI container or code generator unless a later ADR documents measured wiring/lifecycle complexity. - Use explicit constructors; avoid `init()` side effects and mutable globals. - Configuration comes from validated environment variables. Logs go to stdout as structured records without credentials, card keys, or sensitive payloads. - Propagate `context.Context`; bound external calls, queues, batches, and database pools. - Use parameterized queries and transactions with explicit boundaries. Stream/bulk-load the multi-million-row input instead of holding it in memory. - Use Ent for application persistence and relationship modeling. Use pgx with PostgreSQL `COPY` for the bulk-import staging path; do not force high-volume ingestion through row-by-row ORM operations. - Database migrations are explicit, versioned, reviewed artifacts applied by a dedicated migration/deployment step. Never run Ent automatic schema creation against production during application startup. - Passwords use Argon2id with reviewed parameters and a unique salt. Sessions are server-side and revocable; browsers receive only an opaque token in a `Secure`, `HttpOnly`, `SameSite=Lax` cookie. Never put authentication tokens in browser storage. - Required verification includes unit/integration tests, race detection where applicable, `go vet`, `golangci-lint`, `govulncheck`, and migration/import tests. ## Git and documentation - Use atomic Conventional/Commitizen commits. - Track specs, ERD/schema definitions, migrations, importer code, validation rules, manifests/templates, and runbooks. - Ignore raw CSV archives, database volumes/dumps, credentials, and generated runtime data. - Changes to a metric, API, schema, authorization rule, or AI data boundary require the corresponding spec/ADR update. ## AI boundary (post-MVP) AvalAI is accessed only from the backend using a server-held API key. Prefer the simplest bounded LLM call/workflow that satisfies the approved use case. Send minimized, sanitized aggregates with explicit budgets/timeouts; persist trace metadata and deterministic inputs, not unnecessary sensitive prompts. AI output is advisory and must cite underlying deterministic metrics.