e00e264252
Each cost component's knob (cost_per_trade / slip_vol_mult / carry_per_cycle) now accepts one number for every cell — today's form, byte-identical on the wire so stored documents keep their content ids (CostValue's custom serde, the Axis precedent) — or an instrument-keyed map resolved per cell at member construction. A mixed-scale matrix (GER40 ~2e4, EURUSD ~1.1) keeps consistent cost units in ONE document: the N-copies workaround retires and generalize-under-constant-costs becomes expressible again. Validation is strict both ways at the intrinsic tier: a map missing a campaign instrument, or naming one the campaign does not list, refuses at validate with prose naming the component and instruments (no silent default — a partial map would reproduce exactly the silent unit inconsistency the issue reports). The instrument threads through cost_knob/cost_nodes_for/run_blueprint_member from the cell (run side and persist re-run side receive the same value — the C1 drift-alarm lockstep); the manifest stamps the resolved per-cell value under the unchanged cost[k].<knob> key, and the sugar/reproduce paths pass an inert instrument (their specs are scalar by construction). Fork decisions and the discarded first attempt: issue #260 comments. New coverage: serde round-trips (map form; scalar stays bare), validate cross-check units, both prose directions pinned, and two hostless e2es over the synthetic SYMA+SYMB archive — per-cell resolution visible in the member manifests, and the validate refusal. closes #260