# Reliable report automation lab

Original synthetic, local-only reporting workflow. No source account, scheduler, email system or production destination is connected. Python 3.12 standard library; tests record the actual runtime.

## Reproduce

From this directory:

```powershell
python build_fixture.py
python verify.py
python pipeline.py --data events.csv --source-manifest source-manifest.json --config report-config.json --output example-runs
```

The final command prepares a local bundle with `metrics.json`, `selected-events.csv` and `manifest.json`. It does not send or publish anything. The CLI returns zero on success and one on a handled validation/IO failure, with a structured diagnostic. Argument parsing has its own nonzero exit for invalid invocation.

## Metric and source contract

Report period: January 5, 2026 at 00:00 UTC inclusive to January 12 at 00:00 UTC exclusive. Select `paid` events by occurrence time. A one-hour extraction allowance captures the supplied late-arriving E2 event. Region is an allowlisted parameter: all, North, South or Unknown.

Seven raw rows contain six unique events and one identical replay. E1 is exactly at the lower boundary and is included. E2 occurs before the upper boundary but arrives afterward, within the declared extract. E3 is exactly at the exclusive upper boundary and is excluded; E4 precedes the period; E6 is canceled. E1/E2/E5 contribute 1,000/2,000/500 paise, totaling 3,500 paise (₹35) across three events.

The source manifest declares schema version, row count, data SHA-256, event watermark and extraction time. These checks identify inconsistent exports; the locally generated manifest is not independent proof that an upstream system supplied every event. Watermark reliability and grace-period policy need operational evidence in a real system. A later correction is a new input version, not an invisible overwrite.

## Reruns and local bundle integrity

Run identity hashes source bytes, source-manifest bytes, canonical configuration, pipeline code and Python version. An identical rerun verifies the existing bundle and reuses it. Changed evidence creates a different run directory. Existing corrupted output fails verification rather than being overwritten.

Files are prepared in a temporary sibling directory and the completed bundle is renamed into place on the same filesystem. The tests verify that injected failures before that step expose no completed bundle. This does not establish power-loss durability, cross-filesystem atomicity, object-store behavior or comprehensive concurrency correctness. Hashes detect accidental byte changes against the manifest; they are not signatures or protection against an actor rewriting both files and manifest.

Generated manifest status is `prepared_for_review`, distribution `not_sent`. Human approval and distribution semantics will be separate teaching components; preparing a bundle is not authorization to distribute it.

## Checks

`verify.py` covers the cutoff and golden totals, exact rerun reuse, changed parameters, truncated/changed source bytes, row-count mismatch, stale watermark, extraction outside the allowance, conflicting IDs, invalid amounts and parameters, invalid chronology, two injected staging failures, corrupt output, explicit-clock freshness and a CLI failure from a different working directory.

The implementation is deliberately bounded: in-memory CSV processing, a small controlled event schema, local filesystem bundles and no network. It does not implement a production scheduler, database transaction, secret store, API client or messaging service.

`scheduled_run.py` is an optional local wrapper. It launches the same Python interpreter with absolute source/config/output paths, writes one attempt log, preserves the child exit code and imposes a timeout. Tests exercise successful and failed local invocations. No Windows task or other scheduler is registered, and no external alert is sent. A real schedule must supply the correct period-specific configuration; repeatedly using the supplied January fixture configuration intentionally reruns that same period.

## Simulated API extension

`api_extract.py` defines a separate, explicitly fictional cursor-API contract. Pages carry typed items, a snapshot ID, a total unique-item count and a next cursor; only `None` marks the end. The fixture includes a repeated item and a final empty page. It is not a client for a particular vendor.

Run `python verify_api.py` for simulated retry, schema, pagination, snapshot and rate-limit checks. Injected waits and clocks make the tests deterministic; no actual sleep or network request occurs. Read retries are bounded and limited to selected transient failures. Numeric and HTTP-date Retry-After values are honored; a delay beyond the local wait budget defers rather than retrying early. There is no distributed rate limiter or fleet jitter implementation.

Collection returns only after all pages validate and the declared unique count reconciles. A failed collection returns no partial result, although it uses memory and does not implement a persistent checkpoint. Identical repeated IDs collapse; conflicting IDs, cursor cycles and snapshot changes fail. Stable snapshots and trustworthy totals are assumptions of this teaching API, not guarantees shared by all real APIs.
