# Enron source preparation

The public `corbt/enron-emails` dataset is the first real-source connector.
Its preparation API downloads and verifies a complete, explicitly
pinned revision. It also reuses a verified local cache without network access:

```python
from pathlib import Path
from pipewhy.adapters.enron import EnronConfiguration, prepare_source

source = prepare_source(EnronConfiguration(
    cache=Path("enron-cache"),
    revision="cfc06c758093d90993abce1a43668fb7357258a6",
    offline=False,
))
```

This revision contains 517,401 records in three Parquet files (493,482,449 bytes).
Preparation validates the pinned metadata, sizes and SHA256 digests before
publishing a complete cache manifest. Set `offline=True` to require cached history;
missing or corrupt files fail explicitly. Defaults allow 512 MiB of compressed
files per requested revision and 4 MiB per metadata response. Cache removal is
explicit; preparation never evicts other revisions. Configuration construction
does not connect or read ambient authentication tokens. No email text is logged.

After `poetry sync --extras enron`, stream the verified source into bounded C2
record buffers:

```python
from pipewhy.adapters.enron import iter_record_batches

record_count = sum(
    len(memoryview(batch.offsets)) for batch in iter_record_batches(source)
)
```

The reader validates all nine Arrow fields, preserves nulls and recipient ordering,
and converts UTC microseconds exactly. Defaults bound Arrow batches to 128 rows and
emitted C2 pools to 1,048,576 uint64 words with at most 65,536 records. A single
over-limit record fails explicitly. A full iteration checks shard contents and
file identity before/after reading and requires the declared record count. Stopping
iteration early only reads a prefix; it does not establish complete coverage.
`EnronReadError.records_seen` retains progress on a failed read without exposing
email values. The optional reader is PyArrow 26.0.0; importing the base package
does not import or require it.

`EnronAdapter(source)` exposes offline capabilities, bounded artifact discovery
and pinned reads. `discover()` returns repository artifact 0 and train artifact 1;
`discover(1)` selects the dataset. `lineage()` returns no edges with `UNKNOWN`
coverage and `UPSTREAM_LINEAGE_NOT_PROVIDED`. Shard paths and cache locations do
not determine artifact IDs. These methods operate on the prepared source and
never make network requests.

To scan the prepared cache from this checkout:

```powershell
poetry sync --extras cpu --extras enron
poetry run pipewhy scan enron --config examples/enron.toml --source-version cfc06c758093d90993abce1a43668fb7357258a6 --backend cpu --format json
```

The example requires cached files (`offline=true`); set `offline=false` to permit
explicit preparation during the scan. Cache and snapshot paths are relative to
the configuration file. The source accepts optional positive `source_limit` and
`metadata_limit` byte limits. `reference`, `cpu`, `cuda` and `auto` use the existing
backend rules. Dataset retention is the default; `--retain value` explicitly saves
normalized email records. Missing optional dependencies or preparation failures
exit 3 without a successful capture. A mid-read failure saves an unavailable
observation (exit 4) with its reason/progress, discarding partial fingerprints and
retained values. Processing failures close active source iterators.

Snapshots include pinned shard identities and unknown-lineage evidence. Reopen
them with `--store .artifacts/enron/snapshots`; saved queries need neither PyArrow,
NumPy nor access to the cache. An unchanged complete dataset self-diff exits 0;
lineage exits 4 because upstream pipeline lineage is unknown. Source mail dates
are never observation/operation clocks. See [blueprint/ENRON_CONTRACT.txt](../development/architecture.rst) and
[blueprint/ENRON_ACCEPTANCE.txt](../development/architecture.rst) for exact mapping, qualification and limitations.

Incident exports preserve their lineage context in `coordination.lineage_context`
(`cli-export-context-v2`), including unknown coverage and its reasons. JSON, CSV
companions and text retain these qualifiers; incomplete lineage makes incident
export exit 4 even for an empty incident table. DOT retains the complete lineage
page as base64 UTF8 JSON in `pipewhy_metadata_base64`.
