feat(engine): serde foundation + amend C16 dependency policy (cycle D / iter 1)

Iteration 1 of the run-registry cycle (#33): lay the dependency + serde
foundation the registry's typed read-path needs, and amend the contract that
forbade it.

Contract change (load-bearing — C18/C16). C16's blanket "zero-external-
dependency by commitment" + "aura-ingest is the sole external-dependency
firewall" framing is struck and replaced by a considered, per-case dependency
policy: dependencies are admitted by deliberate review (what a crate pulls in
vs. what it buys), with particular scrutiny for anything entering the frozen
deploy artifact (C13); standard vetted crates (serde, rayon, ...) pass that
review and are used wherever they do the job, including in the bot; hand-rolling
what a vetted crate does is the anti-pattern. C16's engine/project split +
three-tier node reuse are unchanged. The five source comments that asserted the
struck framing (aura-engine/aura-ingest Cargo.toml + aura-ingest/report.rs docs)
are rewritten to match; no live source or ledger text still asserts it.

Per-case justification for serde (the policy now requires one): it gives the
run-report types a typed (de)serialization path — exactly what ranking/compare
over stored runs needs, and what the writer-only hand-rolled JSON (C14) could
never provide; closes the typed-read-path gap (#17). Its closure is tiny,
ubiquitous, heavily audited, and its output deterministic (C1-safe). serde_json
is test-only this iteration (dev-dependency); no production serde_json yet.

Changes: [workspace.dependencies] serde (derive) + serde_json; serde derives on
Timestamp (aura-core) and RunMetrics/RunManifest/RunReport (aura-engine), with
serde_json round-trip tests (window renders as a [from,to] integer array via the
transparent Timestamp newtype). The hand-rolled to_json writers are untouched
(still drive stdout). No aura-registry crate, no SweepPoint change — those are
iterations 2 and 3.

Verified: cargo test --workspace green (incl. the two new round-trip tests);
clippy --workspace --all-targets -D warnings clean; no surviving assertion of
the struck zero-dep framing in live source or the ledger.

refs #33
This commit is contained in:
2026-06-10 20:11:39 +02:00
parent 8159b785a8
commit eec876975c
9 changed files with 118 additions and 27 deletions
+4
View File
@@ -6,3 +6,7 @@ license.workspace = true
publish.workspace = true
[dependencies]
serde = { workspace = true }
[dev-dependencies]
serde_json = { workspace = true }
+12 -1
View File
@@ -4,7 +4,7 @@
/// Canonical engine time: epoch-nanoseconds UTC. Newtype over `i64` (C7).
/// `Default` (epoch 0) lets `Column<Timestamp>` pre-size its ring.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize)]
pub struct Timestamp(pub i64);
/// The kind tag of a scalar / column, used for the edge-time type check (C7).
@@ -108,4 +108,15 @@ mod tests {
assert_eq!(a, b);
assert_eq!(s, a);
}
#[test]
fn timestamp_serde_round_trips() {
let ts = Timestamp(1_700_000_000_000_000_000);
let json = serde_json::to_string(&ts).expect("serialize Timestamp");
// a newtype struct serializes transparently as its inner i64 — this is
// what lets RunManifest's `window` render as a [from, to] integer array
assert_eq!(json, "1700000000000000000");
let back: Timestamp = serde_json::from_str(&json).expect("deserialize Timestamp");
assert_eq!(back, ts);
}
}