Files
zarinpal-analytics-backend/tasks/todo.md

549 lines
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ZarinPal Merchant Analytics Backend Task List
- Status: Approved — Phase 4 implementation authorized 2026-08-20
- Plan: `tasks/plan.md`
- Contract: `docs/API-SPEC.md` and `docs/openapi.yaml` version `1.0.0`
Every task must satisfy its acceptance criteria and the standing Definition of Done. File lists are expected blast radii, not permission to modify unrelated files. Foundation tasks use `F` identifiers; the previously approved API tasks retain their stable numeric identifiers.
## Task F1: Establish the Go workspace and quality toolchain
**Description:** Create the thin command-root/modular-monolith skeleton, validated configuration boundary, structured logging baseline, and pinned repository quality commands without implementing product behavior.
**Acceptance criteria:**
- [ ] `cmd/api`, `cmd/worker`, `cmd/importer`, and migration/admin composition roots are thin and business packages live under `internal/`.
- [ ] Manual constructor wiring, context propagation, validated environment configuration, and safe structured logs follow approved ADRs/AGENTS.
- [ ] Pinned build/test/race/vet/lint/vulnerability/generation commands are documented and reproducible.
**Verification:** Run every scaffold quality command from a clean checkout; confirm no service starts implicitly and dependency/checksum diffs are approved.
**Dependencies:** Explicit implementation authorization.
**Files likely touched:** `go.mod`, `go.sum`, `cmd/`, `internal/platform/`, quality-tool configuration.
**Estimated scope:** Medium (split command roots if more than five files)
## Task F2: Implement the normalized Ent model and migration baseline
**Description:** Translate the approved physical schema into Ent entities/relationships and reviewed Atlas SQL migrations, including identity, import, payment, calendar, and audit tables without cache-revision triggers.
**Acceptance criteria:**
- [ ] Tables, FKs, checks, unique/partial indexes, nullability, restrictive deletion, and 30/90-day audit retention match the physical schema.
- [ ] `payment_sessions.import_run_id` is required; `NoAttempt` never becomes an attempt; terminal verification method and session-grain money are stored once.
- [ ] Clean creation and supported upgrade migrations are versioned, reviewed, and never run from API startup.
**Verification:** Run zero-to-head/upgrade migration tests, schema introspection/ERD comparison, constraint negatives, and representative query-plan checks.
**Dependencies:** F1.
**Files likely touched:** `internal/data/schema/`, `ent/schema/`, `migrations/`, migration tests, Atlas configuration.
**Estimated scope:** Medium slices by schema group
## Task F3: Implement pinned archive preflight and bounded staging
**Description:** Build the one-shot importer boundary that validates the fixed file/checksum/schema/resource caps and streams strict CSV/gzip records through pgx `COPY` into run-scoped staging.
**Acceptance criteria:**
- [ ] Fixed filename, SHA-256, 22-column schema, UTF-8/gzip integrity, byte/row/record caps, and regular read-only path checks fail closed.
- [ ] Parsing is bounded-memory, independent of file/session order, and records safe row numbers without logging source values.
- [ ] One PostgreSQL advisory lock prevents concurrent validate/import/recovery commands.
**Verification:** Unit/fuzz tests cover malformed CSV/UTF-8/gzip, overflow, traversal/symlink, expansion, cancellation, caps, and redaction; staging load stays within measured memory.
**Dependencies:** F1–F2.
**Files likely touched:** `cmd/importer/`, `internal/importing/preflight/`, `internal/importing/staging/`, focused tests.
**Estimated scope:** Medium, split preflight and staging commits
## Task F4: Implement validation, atomic promotion, reports, and recovery
**Description:** Add set-based relationship validation, warning/fatal classification, exact reconciliation, transactional normalization, redacted reports, staging cleanup, no-op detection, and restart-from-source recovery.
**Acceptance criteria:**
- [ ] Any fatal issue promotes zero normalized facts; documented timestamp/sequence/null anomalies remain unchanged warnings.
- [ ] Pinned counts and per-merchant/status reconciliations pass before the sole import run becomes completed.
- [ ] Success/failure removes raw staging; interrupted recovery cleans only its run scope and restarts from the immutable archive.
**Verification:** Full-archive rehearsal plus relationship/rollback/fault-injection/concurrency/no-op/recovery/leak tests; JSON and Markdown reports reconcile.
**Dependencies:** F3.
**Files likely touched:** `internal/importing/validation/`, `internal/importing/promotion/`, `internal/importing/report/`, recovery command/tests.
**Estimated scope:** Medium slices; never one XL commit
## Task F5: Implement synthetic merchant profiles and demo-account seeding
**Description:** Generate deterministic non-sensitive merchant display profiles and exactly one initial demo user per merchant through a separate secure administrative flow.
**Acceptance criteria:**
- [ ] Display names are deterministic, clearly synthetic in storage, and stable across identical imports.
- [ ] Usernames are unique and credentials use approved Argon2id hashing; plaintext is emitted once only to an explicit protected output.
- [ ] Re-running the seed is idempotent and never derives passwords from merchant/source keys.
**Verification:** Determinism/idempotency/hash/permission/redaction tests and two-merchant credential smoke fixture.
**Dependencies:** F2 and completed F4 provider behavior.
**Files likely touched:** `cmd/admin/`, `internal/identity/seeding/`, profile generator/tests, operator output adapter.
**Estimated scope:** Medium (3–5 files)
## Task F6: Implement password authentication and server-side sessions
**Description:** Implement demo credential verification, generic throttled outcomes, two-active-session enforcement, opaque token digests, CSRF binding, expiry/activity coalescing, logout/revocation, and authentication audit retention.
**Acceptance criteria:**
- [ ] Authentication never reveals account existence and returns tenant context only from a valid server-side session.
- [ ] Cookie/token/CSRF, idle/absolute expiry, revocation, disabled account, and two-session behavior match Identity spec.
- [ ] Raw credentials/tokens/IP/user-agent values are absent from storage, logs, and audit evidence.
**Verification:** Table/race/integration tests cover enumeration, throttles, fixation, rotation, expiry, revocation, concurrency, and 30-day cleanup.
**Dependencies:** F2 and F5.
**Files likely touched:** `internal/identity/`, session repository, throttling/audit adapters, focused tests.
**Estimated scope:** Medium slices
## Task F7: Implement reporting periods and deterministic metric queries
**Description:** Build calendar-period discovery and SQL/Ent query services for catalogue `0.4.0`, preserving session grain, missingness, Saturday–Friday/Jalali/season boundaries, and peer suppression/equal weighting.
**Acceptance criteria:**
- [ ] Every catalogue metric reconciles for day/week/month/season and emits unit, denominator/sample/coverage, dataset snapshot, metric version, and calculation time.
- [ ] `Paid` plus `Verified`, verification completion, retry recovery, timing percentiles, fees, and `NoAttempt` semantics match approved formulas.
- [ ] Peer outputs exclude the active merchant, weight merchants equally, invert lower-is-better percentiles, and suppress below both thresholds.
**Verification:** Independent SQL oracle tests, boundary fixtures, large/skewed merchant fixtures, null/zero/unknown-status tests, and measured query plans.
**Dependencies:** F2 and trusted F4 dataset fixtures.
**Files likely touched:** `internal/analytics/periods/`, `internal/analytics/metrics/`, peer queries, fixtures/tests.
**Estimated scope:** Medium slices by section
## Task F8: Implement Redis/Asynq analytics run orchestration
**Description:** Implement idempotent four-section runs, dataset-versioned 30-day cache keys, Asynq `v0.26.0` jobs, bounded concurrency, retries, rate limits, event history, and sibling-failure isolation.
**Acceptance criteria:**
- [ ] Equivalent starts single-flight; all sections begin in bounded parallel and publish ready results atomically/independently.
- [ ] Attempts time out at 60 seconds and only transient failures receive two exponential-backoff/jitter retries.
- [ ] Redis loss yields safe cold recomputation; one-hour events/15-second heartbeat and independent user/merchant limits match specs.
**Verification:** Queue/cache integration, race, failure, retry timing, Redis-loss, deduplication, replay, rate-limit, and sensitive-payload tests.
**Dependencies:** F1, F6, and F7.
**Files likely touched:** `internal/analytics/runs/`, `internal/analytics/jobs/`, Redis adapters, worker root/tests.
**Estimated scope:** Medium slices
## Task F9: Implement tenant-scoped detail and export providers
**Description:** Build the shared authorized query/projection for unverified-paid, failed, and retried details, deterministic keyset pagination, export preflight/count/stream inputs, and operator audit metadata.
**Acceptance criteria:**
- [ ] Every query requires session-derived merchant scope and approved period/filter allowlists.
- [ ] Detail/export projections reconcile to metrics and exclude payer tokens, peer rows, credentials, and internal keys.
- [ ] Stable ordering, cursor inputs, 100,000-row/50-MiB/60-second export caps, and audit metadata are available to the API adapter.
**Verification:** Tenant/tampering/reconciliation/pagination/snapshot/cap/query-plan/leak tests against PostgreSQL.
**Dependencies:** F2, F6, and F7.
**Files likely touched:** `internal/analytics/details/`, cursor/projection package, export preflight, audit/tests.
**Estimated scope:** Medium slices
## Task F10: Stabilize provider interfaces and backend integration
**Description:** Wire Identity, period, analytics-run/event, detail/export, audit, PostgreSQL, and Redis providers behind narrow interfaces consumed by the Merchant API.
**Acceptance criteria:**
- [ ] Composition roots wire explicit constructors with bounded pools/timeouts and graceful lifecycle.
- [ ] Provider contracts match OpenAPI semantics without importing HTTP types into domain packages.
- [ ] A non-HTTP integration harness completes seed/login/period/run/partial-result/detail/export-provider flows for two isolated merchants.
**Verification:** Full provider integration, race, shutdown, dependency-loss, tenant-boundary, and safe-log tests.
**Dependencies:** F4–F9.
**Files likely touched:** provider interfaces/adapters, `cmd/api/`, `cmd/worker/`, integration tests.
**Estimated scope:** Medium (split wiring and integration)
## Checkpoint F: Backend provider readiness
- [ ] Tasks F1–F10 and every foundation acceptance criterion pass.
- [ ] Full pinned import, two-merchant identity, metric reconciliation, Redis-loss, and tenant isolation evidence is reviewed.
- [ ] Human approves provider readiness before completing HTTP feature slices.
## Task 1: Establish the OpenAPI toolchain and conformance harness
**Description:** Select, approve, and pin the Go OpenAPI generation/validation tooling; make contract validation and generated-artifact drift checks reproducible from a clean checkout.
**Acceptance criteria:**
- [ ] One approved pinned toolchain validates OpenAPI 3.1 and produces only the intended server/types artifacts.
- [ ] Generated artifacts contain no hand-edited business logic and regenerate with a clean Git diff.
- [ ] Stable `operationId` values, schemas, media types, and security requirements are covered by a contract test harness.
**Verification:**
- [ ] Run the documented OpenAPI validate/generate/check commands from a clean checkout.
- [ ] Run focused generator/conformance tests and confirm a deliberate contract mismatch fails.
- [ ] Review generated and lockfile/checksum diffs; approve any new dependency before installation.
**Dependencies:** Explicit implementation authorization and F1.
**Files likely touched:**
- `go.mod`
- `go.sum`
- `internal/api/contract/`
- `internal/api/contract_test.go`
- approved command/tool configuration
**Estimated scope:** Medium (3–5 files/artifact groups)
## Task 2: Build the shared HTTP boundary and protected-request middleware
**Description:** Create the API server boundary for `/api/v1`, central Problem Details mapping, request ids, 16 KiB body/8 KiB target limits, no-store/security headers, session resolution, and CSRF/origin enforcement.
**Acceptance criteria:**
- [ ] All protected handlers receive merchant/user context only from a verified server-side session.
- [ ] Boundary failures use the approved RFC 9457 schema/code/status mapping without internal details.
- [ ] Request limits, unknown fields/parameters, CSRF/origin checks, and no-store/security headers behave as specified.
**Verification:**
- [ ] Focused middleware tests cover missing/expired/revoked sessions, malformed input, size limits, request ids, CSRF, and origin/fetch metadata.
- [ ] Cross-tenant client inputs cannot create or replace merchant context.
- [ ] `go test ./...` and `go vet ./...` pass.
**Dependencies:** Task 1 and F6 provider interface.
**Files likely touched:**
- `internal/api/http/server.go`
- `internal/api/http/problem.go`
- `internal/api/http/middleware.go`
- `internal/api/http/middleware_test.go`
**Estimated scope:** Medium (4 files)
## Checkpoint 1: Boundary readiness
- [ ] Tasks 1–2 acceptance criteria pass.
- [ ] OpenAPI generation is reproducible with a clean diff.
- [ ] Human review confirms the tenant and error boundaries before feature handlers begin.
## Task 3: Implement authentication session endpoints
**Description:** Implement login, current-session, and logout as one vertical slice through the approved Identity provider, including secure cookie and generic-failure behavior.
**Acceptance criteria:**
- [ ] Login creates a revocable session and sets only the approved `__Host-zpa_session` cookie attributes.
- [ ] Current-session JSON matches OpenAPI and exposes a session-bound CSRF token but no authentication token.
- [ ] Logout revokes server state before clearing the cookie; expected credential failures remain indistinguishable and throttled.
**Verification:**
- [ ] Integration tests cover success, enumeration resistance, cookie flags, rotation, expiry, revocation, throttling, CSRF, and dependency failure.
- [ ] OpenAPI conformance tests cover all auth statuses and headers.
- [ ] `go test -race ./...` passes for affected packages.
**Dependencies:** Task 2 and F6.
**Files likely touched:**
- `internal/api/http/auth_handler.go`
- `internal/api/http/auth_handler_test.go`
- `internal/api/identity.go`
- `cmd/api/main.go`
**Estimated scope:** Medium (4 files)
## Task 4: Implement approved reporting-period discovery
**Description:** Return only allowlisted day/week/Jalali-month/Persian-season periods with exact UTC/Tehran boundaries, ready Persian labels, and the approved default.
**Acceptance criteria:**
- [ ] Period responses match OpenAPI and use backend-calculated half-open boundaries.
- [ ] Iranian weeks start Saturday; default selection is the latest complete Jalali month containing source data.
- [ ] Unknown parameters/types are rejected and arbitrary date ranges are impossible.
**Verification:**
- [ ] Table tests cover all period types, month/year boundaries, Saturday–Friday weeks, empty source ranges, and timezone independence.
- [ ] Integration tests reconcile returned ids/boundaries with `calendar_dates`.
- [ ] OpenAPI examples and `go test ./...` pass.
**Dependencies:** Task 2 and F7.
**Files likely touched:**
- `internal/api/http/period_handler.go`
- `internal/api/http/period_handler_test.go`
- `internal/api/periods.go`
**Estimated scope:** Medium (3 files)
## Task 5: Implement analytics-run start and snapshot retrieval
**Description:** Implement idempotent run creation/current-run resolution and tenant-scoped snapshot retrieval for all four sections.
**Acceptance criteria:**
- [ ] Same intent/key replays safely, key/body conflicts fail, and equivalent concurrent starts resolve one current run.
- [ ] Run snapshots serialize every section state/result exactly as OpenAPI defines.
- [ ] Missing and cross-tenant run ids return the same `404 RESOURCE_NOT_FOUND` response.
**Verification:**
- [ ] Concurrency tests cover atomic idempotency claim, in-flight duplicates, the immutable dataset snapshot, and no duplicate jobs.
- [ ] Contract tests cover `200`, `202`, `401`, `403`, `404`, `422`, `429`, and dependency failures.
- [ ] Integration tests assert that rejected/limited requests enqueue no work.
**Dependencies:** Tasks 1–2 and F8.
**Files likely touched:**
- `internal/api/http/run_handler.go`
- `internal/api/http/run_handler_test.go`
- `internal/api/analytics.go`
- `internal/api/idempotency.go`
**Estimated scope:** Medium (4 files)
## Checkpoint 2: Core API flow
- [ ] Tasks 3–5 acceptance criteria pass.
- [ ] Authenticated merchant flow works through the actual HTTP server and test dependencies.
- [ ] Cross-tenant and idempotency adversarial tests pass.
- [ ] Human review approves the core flow before streaming/detail work.
## Task 6: Implement authenticated SSE delivery and recovery
**Description:** Stream typed run/section events with 15-second heartbeats, one-hour authorized replay, snapshot reset, and session-aware disconnect/reconnect behavior.
**Acceptance criteria:**
- [ ] Event framing, ids, names, and JSON data match the approved SSE schemas and UTF-8 media type.
- [ ] Retained ids replay only later authorized events; unavailable ids reset with `run.snapshot` and never disclose another tenant.
- [ ] Session expiry/revocation, connection limits, and completed-run closure behave as specified.
**Verification:**
- [ ] Integration tests cover ordered partial readiness, failure isolation, suppression, heartbeat timing, replay, reset, disconnect, revocation, and user/merchant connection limits.
- [ ] Proxy-style tests assert no buffering/compression and required stream headers.
- [ ] Race tests cover concurrent publish/replay/disconnect paths.
**Dependencies:** Task 5 and F8.
**Files likely touched:**
- `internal/api/http/sse_handler.go`
- `internal/api/http/sse_handler_test.go`
- `internal/api/events.go`
- `internal/api/events_test.go`
**Estimated scope:** Medium (4 files)
## Task 7: Implement exhausted-section manual retry
**Description:** Add the idempotent retry-attempt subresource for one exhausted failed section without disturbing ready siblings.
**Acceptance criteria:**
- [ ] Only an exhausted, retryable failed section for the current run/dataset snapshot is accepted.
- [ ] Retry idempotency and independent user/merchant throttles prevent duplicate work.
- [ ] Ready siblings remain unchanged and stale/cross-tenant requests fail safely.
**Verification:**
- [ ] Tests cover retryable/non-retryable states, duplicate keys, stale dataset snapshots, throttling, cross-tenant ids, and no sibling restart.
- [ ] Rejected retries enqueue no work.
- [ ] OpenAPI conformance and `go test -race ./...` pass.
**Dependencies:** Tasks 5–6.
**Files likely touched:**
- `internal/api/http/retry_handler.go`
- `internal/api/http/retry_handler_test.go`
- `internal/api/retry.go`
**Estimated scope:** Medium (3 files)
## Checkpoint 3: Asynchronous behavior
- [ ] Tasks 6–7 acceptance criteria pass.
- [ ] SSE and retry runtime checks pass through the actual queue/cache test boundary.
- [ ] Human review confirms recovery and rate-limit behavior.
## Task 8: Implement cursor-paginated session detail views
**Description:** Implement the three approved detail views with deterministic ordering, opaque bound cursors, default 25/maximum 100 rows, and a strict field allowlist.
**Acceptance criteria:**
- [ ] `UNVERIFIED_PAID`, `FAILED`, and `RETRIED` filters match catalogue definitions and reconcile to aggregates.
- [ ] Cursors bind tenant, canonical filters, ordering, expiry, and last row; invalid reuse returns generic `INVALID_CURSOR`.
- [ ] Detail rows expose only approved fields and the tenant-scoped opaque terminal reference.
**Verification:**
- [ ] Tests cover page boundaries, duplicate timestamps, cursor tampering/expiry/filter change/cross-tenant reuse, limits, and empty results.
- [ ] Integration queries reconcile representative merchants and approved periods.
- [ ] Sensitive-field absence and measured query-plan checks pass.
**Dependencies:** Tasks 2 and 4 plus F9.
**Files likely touched:**
- `internal/api/http/detail_handler.go`
- `internal/api/http/detail_handler_test.go`
- `internal/api/details.go`
- `internal/api/cursor.go`
- `internal/api/cursor_test.go`
**Estimated scope:** Medium (5 files)
## Task 9: Implement safeguarded Persian CSV export
**Description:** Stream the same authorized detail predicate/projection as Persian CSV with BOM, Persian spaced headers/statuses, machine-safe numeric/ISO values, formula neutralization, and approved caps.
**Acceptance criteria:**
- [ ] CSV and JSON share one canonical tenant/filter/projection policy; CSV adds no unavailable field.
- [ ] Output matches the approved encoding, headers, labels, quoting, disposition, no-store, and `nosniff` contract.
- [ ] Preflight rejects over 100,000 rows/50 MiB, generation ends within 60 seconds, throttling applies, and interrupted streams are audited as incomplete.
**Verification:**
- [ ] Golden tests cover Persian output, BOM, CRLF, quotes/newlines, all status mappings, and stable headers with spaces.
- [ ] Security tests cover `=`, `+`, `-`, `@`, tab, CR, LF formula prefixes and forbidden-field absence.
- [ ] Integration tests cover filter parity, snapshot consistency, caps, deadline, disconnect, throttle, and content headers.
**Dependencies:** Task 8.
**Files likely touched:**
- `internal/api/http/export_handler.go`
- `internal/api/http/export_handler_test.go`
- `internal/api/export.go`
- `internal/api/export_test.go`
**Estimated scope:** Medium (4 files)
## Checkpoint 4: Reconciliation and data boundary
- [ ] Tasks 8–9 acceptance criteria pass.
- [ ] Detail/CSV reconciliation and sensitive-data negative tests pass.
- [ ] Export resource limits hold under representative merchant volume.
- [ ] Human review approves the merchant evidence boundary.
## Task 10: Publish the stable API problem-type catalogue
**Description:** Document every stable application error code and make each approved `https://zarinpal.amiirkhl.ir/docs/errors/{error-slug}` URI resolve to safe human-readable guidance.
**Acceptance criteria:**
- [ ] Every OpenAPI problem code maps to one stable slug, title, status, meaning, safe resolution guidance, and retry rule.
- [ ] Published pages expose no internal topology, SQL, stack, secret, tenant existence, or security-control detail useful for abuse.
- [ ] Contract tests fail when a returned problem type/code lacks catalogue documentation.
**Verification:**
- [ ] Link checks confirm every production type URI resolves successfully.
- [ ] OpenAPI and catalogue mapping tests pass with no duplicate slug/code.
- [ ] Security review approves public wording.
**Dependencies:** Tasks 2–9 for the final error-code set; hosting mechanism approval.
**Files likely touched:**
- `docs/ERROR-CATALOGUE.md`
- error-page source/template location approved by the hosting plan
- `internal/api/http/problem_catalogue_test.go`
**Estimated scope:** Medium (2–3 files/artifact groups)
## Task 11: Add API audit and observability instrumentation
**Description:** Instrument the approved audit events, metrics, logs, and traces with bounded-cardinality safe metadata and 90-day analytics/export-audit retention.
**Acceptance criteria:**
- [ ] Required auth/run/SSE/detail/export audit outcomes and operational metrics are emitted with correlation ids.
- [ ] Only authorized backend operators can access analytics/export audit records; automatic deletion occurs after 90 days.
- [ ] Telemetry excludes credentials, tokens, raw cursors/filters, payment rows, payer tokens, peer data, and high-cardinality sensitive labels.
**Verification:**
- [ ] Tests assert required event/metric emission for success, rejection, failure, timeout, and interrupted export.
- [ ] Negative leak tests inspect structured logs, traces, and audit records.
- [ ] Retention/access-control tests pass using an accelerated test clock.
**Dependencies:** Tasks 3–10 and the approved audit/observability adapters.
**Files likely touched:**
- `internal/api/observability.go`
- `internal/api/audit.go`
- `internal/api/observability_test.go`
- `internal/api/audit_test.go`
**Estimated scope:** Medium (4 files)
## Task 12: Complete contract, security, race, and performance verification
**Description:** Run and close the full implementation Definition of Done across all API operations and critical failure/abuse paths.
**Acceptance criteria:**
- [ ] Every OpenAPI operation/status/media type/header/security requirement has automated contract coverage.
- [ ] Tenant isolation, session/CSRF, idempotency, SSE, cursor, export, audit, and sensitive-field abuse suites pass.
- [ ] Representative detail/export query plans and resource bounds meet approved behavior with no unreviewed exception.
**Verification:**
- [ ] Run `go test ./...`, `go test -race ./...`, `go vet ./...`, `golangci-lint run`, and `govulncheck ./...`.
- [ ] Run reproducible OpenAPI validation/generation and confirm a clean Git diff.
- [ ] Exercise critical merchant flows end to end through the deployed-style proxy/test environment.
**Dependencies:** Tasks 1–11.
**Files likely touched:**
- API contract/integration test packages
- approved test fixtures containing no sensitive source data
- API runbook/documentation corrections if verification finds drift
**Estimated scope:** Medium (3–5 files/artifact groups)
## Checkpoint 5: Complete
- [ ] Tasks 1–12 and every checkpoint pass.
- [ ] Full project Definition of Done is satisfied.
- [ ] No unrelated frontend, deployment, physical-schema, ERD, or raw-data changes are included.
- [ ] Human review approves completion before merge or deployment.