The per-step diag JSONL `json!{...}` block had grown to 642 leaf paths
across ~30 nested objects, duplicating every ISV slot read into the
example binary. Phase A of the 2026-05-31 checkpoints+eval-diag plan
extracts it into a single builder on the trainer so the eval phase
can reuse it (next commit) without duplicating the schema.
Changes:
* `IntegratedTrainer::build_diag_value(step, elapsed_s, &DiagInputs)
-> Result<serde_json::Value>` — same 642-leaf schema as before,
bit-equivalent ISV reads (all from `self.isv_host_slice()`).
* `DiagInputs<'a>` struct bundles the host-side per-step state the
trainer doesn't own (DiagStaging reads + running counters +
windowed act histogram), so the call site stays a 1-liner.
* Train loop in `alpha_rl_train.rs` swaps the inline json! for the
builder call; the ~140-slot ISV-imports wall collapses to four
slots still read by the stderr ticker.
* `#![recursion_limit = "256"]` moves from the example into
`ml-alpha/src/lib.rs` since the builder now lives in the library.
Schema parity verified: `head -1 diag.jsonl | jq 'paths(scalars)|sort'`
yields the same 642 keys as before this refactor (no schema drift).