plan: 0029 run-registry foundation (iteration 1)
Iteration 1 of cycle D: amend C16 (blanket zero-dep -> per-case policy) in the ledger + the five source comments asserting the struck framing; add serde/ serde_json workspace deps; derive Serialize/Deserialize on Timestamp and the RunMetrics/RunManifest/RunReport report types, with serde_json round-trip tests. No CLI change, no aura-registry crate, no SweepPoint change (those are iter 2/3). refs #33
This commit is contained in:
@@ -0,0 +1,353 @@
|
||||
# Run Registry — Iteration 1 (foundation) — Implementation Plan
|
||||
|
||||
> **Parent spec:** `docs/specs/0029-run-registry-sweep-family.md`
|
||||
>
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: use the `implement` skill to run
|
||||
> this plan. Steps use `- [ ]` checkboxes for tracking.
|
||||
|
||||
**Goal:** Lay the dependency-policy + serde foundation the run registry builds
|
||||
on: amend C16, add `serde`/`serde_json` to the workspace, derive
|
||||
`Serialize`/`Deserialize` on `Timestamp` + the run-report types, with round-trip
|
||||
tests — no CLI change, no new crate, no `SweepPoint` change yet.
|
||||
|
||||
**Architecture:** C16's blanket zero-dependency clause is struck for a per-case
|
||||
policy (ledger + the five source comments that assert the old framing). `serde`
|
||||
lands as a workspace dependency; `Timestamp` (aura-core) and
|
||||
`RunMetrics`/`RunManifest`/`RunReport` (aura-engine) gain serde derives, proven
|
||||
by serde_json round-trip tests. The hand-rolled `to_json` writers are untouched.
|
||||
|
||||
**Tech Stack:** `serde` (derive) + `serde_json` (test-only), `aura-core`,
|
||||
`aura-engine`, the design ledger.
|
||||
|
||||
---
|
||||
|
||||
**Files this plan creates or modifies:**
|
||||
|
||||
- Modify: `docs/design/INDEX.md:535-565` — amend C16 (strike blanket zero-dep / firewall; add per-case policy)
|
||||
- Modify: `crates/aura-ingest/Cargo.toml:10-12` — rewrite the firewall/zero-dep comment
|
||||
- Modify: `crates/aura-ingest/src/lib.rs:10-12` — rewrite the firewall/zero-dep module doc
|
||||
- Modify: `Cargo.toml` (root) — add `[workspace.dependencies]` (serde + serde_json)
|
||||
- Modify: `crates/aura-core/Cargo.toml:8` — `serde` dep + `serde_json` dev-dep
|
||||
- Modify: `crates/aura-core/src/scalar.rs:7` — add serde derives to `Timestamp`
|
||||
- Test: `crates/aura-core/src/scalar.rs` (tests mod ~69-111) — `timestamp_serde_round_trips`
|
||||
- Modify: `crates/aura-engine/Cargo.toml:8-18` — `serde` dep, rewrite comment, `serde_json` dev-dep
|
||||
- Modify: `crates/aura-engine/src/report.rs:6-13,14,33,52` — serde derives on the three report types + doc-comment fixes
|
||||
- Test: `crates/aura-engine/src/report.rs` (tests mod ~198-413) — `runreport_serde_round_trips`
|
||||
|
||||
---
|
||||
|
||||
### Task 1: C16 amendment + stale-comment sweep (contract/docs, no test)
|
||||
|
||||
**Files:**
|
||||
- Modify: `docs/design/INDEX.md:549-562`
|
||||
- Modify: `crates/aura-ingest/Cargo.toml:10-12`
|
||||
- Modify: `crates/aura-ingest/src/lib.rs:10-12`
|
||||
|
||||
- [ ] **Step 1: Amend the C16 body in the ledger**
|
||||
|
||||
In `docs/design/INDEX.md`, replace this exact block (lines 551-557, from
|
||||
"`aura-ingest` is the" through "firewalled there."):
|
||||
|
||||
```
|
||||
data-source ingestion edge, cycle 0011). `aura-ingest` is the
|
||||
**external-dependency firewall**: it alone links external crates (via
|
||||
`data-server`), keeping the other engine crates and the frozen deploy artifact
|
||||
(C13) dependency-pure (see External components). The engine workspace is
|
||||
**zero-external-dependency** by commitment — the hand-rolled JSON of C14/C18 and
|
||||
the from-scratch `aura-core` substrate exist precisely to honour it; the one
|
||||
sanctioned external tree enters at the ingestion edge and is firewalled there.
|
||||
```
|
||||
|
||||
with:
|
||||
|
||||
```
|
||||
data-source ingestion edge, cycle 0011, where the `data-server` external tree
|
||||
enters). **Dependency policy (amended 2026-06-10, cycle 0029).** Dependencies
|
||||
are admitted by deliberate, **per-case review** — what a crate pulls in weighed
|
||||
against what it buys — with **particular scrutiny for anything that enters the
|
||||
frozen deploy artifact** (C13: this bot = this commit). Well-established
|
||||
standard crates (`serde`, `rayon`, …) pass that review and are used wherever
|
||||
they do the job, including in the bot. There is **no blanket zero-dependency
|
||||
commitment** and **no blanket admission**; hand-rolling what a vetted standard
|
||||
crate already does is the anti-pattern, not the dependency. This strikes the
|
||||
original *"zero-external-dependency by commitment"* clause and the
|
||||
*"`aura-ingest` is the sole external-dependency firewall"* framing; `aura-ingest`
|
||||
remains the data-source ingestion edge, no longer a dependency wall.
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Strike the dependency clause from C16 Forbids**
|
||||
|
||||
In the same file, in the C16 `**Forbids.**` sentence (lines 558-562), replace:
|
||||
|
||||
```
|
||||
package mechanism); an external (crates.io / git) dependency in any engine crate
|
||||
other than the ingestion-edge crate `aura-ingest`.
|
||||
```
|
||||
|
||||
with:
|
||||
|
||||
```
|
||||
package mechanism). (Dependency admission is governed by the per-case policy
|
||||
above, not a blanket ban.)
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Rewrite the aura-ingest Cargo.toml comment**
|
||||
|
||||
In `crates/aura-ingest/Cargo.toml`, replace lines 10-12:
|
||||
|
||||
```
|
||||
# data-server: aura's first real data source AND its one external dependency
|
||||
# (transitively chrono + regex + zip). Isolated in this crate — the firewall
|
||||
# that keeps aura-core/std/engine zero-external-dep (spec §Architecture).
|
||||
```
|
||||
|
||||
with:
|
||||
|
||||
```
|
||||
# data-server: aura's first real data source. It enters the workspace at the
|
||||
# ingestion edge (transitively chrono + regex + zip). Dependencies are admitted
|
||||
# by the amended C16 per-case policy (INDEX.md), not a blanket zero-dep wall.
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Rewrite the aura-ingest module doc**
|
||||
|
||||
In `crates/aura-ingest/src/lib.rs`, replace lines 10-12:
|
||||
|
||||
```
|
||||
//! This crate is the workspace's **external-dependency firewall**: it is the
|
||||
//! only crate that links `data-server` (and its transitive `chrono`/`regex`/
|
||||
//! `zip`), so `aura-core`/`aura-std`/`aura-engine` stay zero-external-dependency.
|
||||
```
|
||||
|
||||
with:
|
||||
|
||||
```
|
||||
//! This crate is the **data-source ingestion edge**: it links `data-server`
|
||||
//! (and its transitive `chrono`/`regex`/`zip`) and normalizes it into aura's
|
||||
//! columns here. Dependencies follow the amended C16 per-case policy (INDEX.md),
|
||||
//! not a blanket zero-dependency wall.
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Verify no struck framing survives + build is intact**
|
||||
|
||||
Run: `rg -n "external-dependency firewall|zero-external-dependency by commitment" docs/ crates/`
|
||||
Expected: no matches (the only remaining `zero-dependency` mentions are the
|
||||
report.rs doc comments, fixed in Task 3).
|
||||
|
||||
Run: `cargo build --workspace`
|
||||
Expected: builds (doc/comment edits do not affect compilation), exit 0.
|
||||
|
||||
---
|
||||
|
||||
### Task 2: workspace serde deps + `Timestamp` serde (RED-first)
|
||||
|
||||
**Files:**
|
||||
- Modify: `Cargo.toml` (root)
|
||||
- Modify: `crates/aura-core/Cargo.toml:8`
|
||||
- Modify: `crates/aura-core/src/scalar.rs:7`
|
||||
- Test: `crates/aura-core/src/scalar.rs` (tests mod)
|
||||
|
||||
- [ ] **Step 1: Declare the workspace dependencies**
|
||||
|
||||
In the root `Cargo.toml`, after the `[workspace.package]` block (after line 23,
|
||||
`publish = false`), append:
|
||||
|
||||
```toml
|
||||
|
||||
[workspace.dependencies]
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Add serde to aura-core**
|
||||
|
||||
In `crates/aura-core/Cargo.toml`, under `[dependencies]` (line 8), add the dep
|
||||
and a dev-dep block:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
serde = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = { workspace = true }
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Write the failing round-trip test**
|
||||
|
||||
In `crates/aura-core/src/scalar.rs`, inside `#[cfg(test)] mod tests` (after the
|
||||
`use super::*;` at line 71), add:
|
||||
|
||||
```rust
|
||||
#[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);
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Run the test to verify it fails**
|
||||
|
||||
Run: `cargo test -p aura-core timestamp_serde_round_trips`
|
||||
Expected: FAIL — compile error `the trait bound `Timestamp: serde::Serialize` is not satisfied` (the derive is not there yet).
|
||||
|
||||
- [ ] **Step 5: Add the serde derives to `Timestamp`**
|
||||
|
||||
In `crates/aura-core/src/scalar.rs`, replace line 7:
|
||||
|
||||
```rust
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
```
|
||||
|
||||
with:
|
||||
|
||||
```rust
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize)]
|
||||
```
|
||||
|
||||
- [ ] **Step 6: Run the test to verify it passes**
|
||||
|
||||
Run: `cargo test -p aura-core timestamp_serde_round_trips`
|
||||
Expected: PASS (1 passed).
|
||||
|
||||
---
|
||||
|
||||
### Task 3: aura-engine serde + report types + doc fix (RED-first)
|
||||
|
||||
**Files:**
|
||||
- Modify: `crates/aura-engine/Cargo.toml:8-18`
|
||||
- Modify: `crates/aura-engine/src/report.rs:6-13,14,33,52`
|
||||
- Test: `crates/aura-engine/src/report.rs` (tests mod)
|
||||
|
||||
- [ ] **Step 1: Add serde to aura-engine + rewrite the dependency-purity comment**
|
||||
|
||||
In `crates/aura-engine/Cargo.toml`, replace the `[dependencies]` body (lines
|
||||
9-12):
|
||||
|
||||
```toml
|
||||
aura-core = { path = "../aura-core" }
|
||||
# No external dependencies: aura-engine is an engine crate and stays
|
||||
# dependency-pure. The data-server ingestion edge lives in the aura-ingest crate
|
||||
# (the external-dependency firewall — cycle 0011, INDEX.md C16), never here.
|
||||
```
|
||||
|
||||
with:
|
||||
|
||||
```toml
|
||||
aura-core = { path = "../aura-core" }
|
||||
# serde is admitted under the amended C16 per-case dependency policy (INDEX.md):
|
||||
# it gives the run-report types a typed (de)serialization path for the run
|
||||
# registry (cycle 0029). serde's output is deterministic (C1-safe).
|
||||
serde = { workspace = true }
|
||||
```
|
||||
|
||||
Then in the same file's `[dev-dependencies]` block (after `aura-std` at line
|
||||
15/18), add:
|
||||
|
||||
```toml
|
||||
serde_json = { workspace = true }
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Write the failing round-trip test**
|
||||
|
||||
In `crates/aura-engine/src/report.rs`, inside `#[cfg(test)] mod tests` (anywhere
|
||||
after the `use` lines at 200-204; place it right before the closing `}` of the
|
||||
module), add:
|
||||
|
||||
```rust
|
||||
#[test]
|
||||
fn runreport_serde_round_trips() {
|
||||
let report = RunReport {
|
||||
manifest: RunManifest {
|
||||
commit: "abc123".to_string(),
|
||||
params: vec![
|
||||
("sma_fast".to_string(), 2.0),
|
||||
("sma_slow".to_string(), 4.0),
|
||||
("exposure_scale".to_string(), 1.0),
|
||||
],
|
||||
window: (Timestamp(1), Timestamp(6)),
|
||||
seed: 0,
|
||||
broker: "sim-optimal(pip_size=1.0)".to_string(),
|
||||
},
|
||||
metrics: RunMetrics { total_pips: 12.0, max_drawdown: 1.0, exposure_sign_flips: 1 },
|
||||
};
|
||||
let json = serde_json::to_string(&report).expect("serialize RunReport");
|
||||
// window is a 2-element [from, to] array (Timestamp newtype is transparent)
|
||||
assert!(json.contains("\"window\":[1,6]"), "window shape: {json}");
|
||||
let back: RunReport = serde_json::from_str(&json).expect("deserialize RunReport");
|
||||
assert_eq!(back, report);
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Run the test to verify it fails**
|
||||
|
||||
Run: `cargo test -p aura-engine runreport_serde_round_trips`
|
||||
Expected: FAIL — compile error `the trait bound `RunReport: serde::Serialize` is not satisfied` (derives not there yet).
|
||||
|
||||
- [ ] **Step 4: Add the serde derives to the three report types**
|
||||
|
||||
In `crates/aura-engine/src/report.rs`, replace each of these three derive lines:
|
||||
|
||||
- line 14 (above `pub struct RunMetrics`): `#[derive(Clone, Debug, PartialEq)]`
|
||||
- line 33 (above `pub struct RunManifest`): `#[derive(Clone, Debug, PartialEq)]`
|
||||
- line 52 (above `pub struct RunReport`): `#[derive(Clone, Debug, PartialEq)]`
|
||||
|
||||
each with:
|
||||
|
||||
```rust
|
||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Run the test to verify it passes**
|
||||
|
||||
Run: `cargo test -p aura-engine runreport_serde_round_trips`
|
||||
Expected: PASS (1 passed).
|
||||
|
||||
- [ ] **Step 6: Fix the now-false zero-dependency doc comments**
|
||||
|
||||
In `crates/aura-engine/src/report.rs`, replace the module-doc tail (lines 6-8):
|
||||
|
||||
```
|
||||
//! and folds them here. Output is canonical, hand-rolled JSON (C14): the schema
|
||||
//! is tiny, closed, and flat, so the deliberately zero-dependency workspace
|
||||
//! stays zero-dependency.
|
||||
```
|
||||
|
||||
with:
|
||||
|
||||
```
|
||||
//! and folds them here. Output is canonical JSON (C14): the schema is tiny,
|
||||
//! closed, and flat. `to_json` is a hand-rolled writer; the report types also
|
||||
//! derive serde (cycle 0029) for the run registry's typed read-path.
|
||||
```
|
||||
|
||||
and replace the `to_json` doc tail (lines 11-13):
|
||||
|
||||
```
|
||||
/// Render canonical, machine-readable JSON (C14). Hand-rolled — the schema
|
||||
/// is tiny, closed, and flat, so the deliberately zero-dependency workspace
|
||||
/// stays so.
|
||||
```
|
||||
|
||||
with:
|
||||
|
||||
```
|
||||
/// Render canonical, machine-readable JSON (C14). Hand-rolled — the schema
|
||||
/// is tiny, closed, and flat. (The same types also derive serde for the run
|
||||
/// registry's typed read-path, cycle 0029; this writer is kept for stdout.)
|
||||
```
|
||||
|
||||
- [ ] **Step 7: Full workspace gate**
|
||||
|
||||
Run: `cargo test --workspace`
|
||||
Expected: PASS — all tests green, including `timestamp_serde_round_trips` and `runreport_serde_round_trips`.
|
||||
|
||||
Run: `cargo clippy --workspace --all-targets -- -D warnings`
|
||||
Expected: no warnings, exit 0.
|
||||
|
||||
Run: `rg -n "zero-dependency|zero-external-dep|external-dependency firewall" docs/ crates/`
|
||||
Expected: no matches — every assertion of the struck framing is gone.
|
||||
Reference in New Issue
Block a user