# C3 — One merge, at ingestion only **Guarantee.** Heterogeneous timestamped sources are k-way-merged by timestamp into one chronological cycle stream at the ingestion boundary; source-native time units (e.g. data-server's Unix-`time_ms`) are normalized there to the canonical epoch-ns `timestamp` of C7. **Forbids.** Any merge / as-of join *inside* the graph. **Why.** A single ordered timeline is the mechanism that makes heterogeneous-rate sources (news daily-bias + M5 + ticks) causally combinable without leaking the future. Keeping the merge at one boundary keeps the graph semantics simple. ## Current state The k-way merge is `Harness::run`'s per-cycle pick of the live source head with the smallest `(timestamp, source index)` (`crates/aura-engine/src/harness.rs`). Each `Source` yields records in ascending timestamp order — the ingestion precondition the merge relies on — and there is no merge or as-of join anywhere inside the DAG; the engine only routes in-graph edges. The ms→epoch-ns normalization is a single seam, `aura_ingest::unix_ms_to_epoch_ns` (`crates/aura-ingest/src/lib.rs`): the data-source ingestion edge transposes the data-server's Array-of-Structs `M1Parsed` records into aura's Structure-of-Arrays base columns (C7) and normalizes their Unix-millisecond time to the canonical epoch-ns `Timestamp` at this one boundary. Both the eager `load_m1_window` transpose and the lazy streaming `M1FieldSource` route through that same seam. ## See also - [C4](c04-cycle-granularity.md) — the merged stream is the data-driven cycle clock; ties break by source declaration order. - [C2](c02-causality.md) — one ordered timeline is what makes heterogeneous-rate sources combinable without look-ahead. - [C7](c07-scalar-soa.md) — the canonical epoch-ns `Timestamp` and the four SoA base columns the merge normalizes into.