diff --git a/docs/specs/0106-research-artifact-vocabularies.md b/docs/specs/0106-research-artifact-vocabularies.md index fc107bd..4ee8de2 100644 --- a/docs/specs/0106-research-artifact-vocabularies.md +++ b/docs/specs/0106-research-artifact-vocabularies.md @@ -126,8 +126,8 @@ resolution comment): }, "strategies": [ { "ref": { "content_id": "9f3a…" }, - "axes": { "fast": [{"I64": 8}, {"I64": 12}, {"I64": 16}], - "slow": [{"I64": 21}, {"I64": 34}] } } + "axes": { "fast": { "kind": "I64", "values": [8, 12, 16] }, + "slow": { "kind": "I64", "values": [21, 34] } } } ], "process": { "ref": { "content_id": "4e2d…" } }, "seed": 1, @@ -213,13 +213,21 @@ pub struct CampaignDoc { #[derive(Serialize, Deserialize, ...)] pub enum DocRef { ContentId(String), IdentityId(String) } // one-of, typed slots -pub struct StrategyEntry { pub r#ref: DocRef, pub axes: BTreeMap> } +pub struct StrategyEntry { pub r#ref: DocRef, pub axes: BTreeMap } +pub struct Axis { pub kind: ScalarKind, pub values: Vec } +// custom serde: `{"kind": "I64", "values": [8, 12, 16]}` — values are bare +// JSON, parsed AS the declared kind pub enum EmitKind { FamilyTable, SelectionReport } // closed v1 set ``` -Axis cells are typed `aura_core::Scalar` values — the same typed-cell -pattern the op-script binds use (`OpDoc::Add { bind: BTreeMap }`, crates/aura-cli/src/graph_construct.rs:37). +An axis declares its `ScalarKind` **once** — the kind is a property of the +parameter, not of each value (user veto, 2026-07-03, of a per-cell tagged +draft), so a mixed-kind axis is unrepresentable rather than merely refused. +The op-script binds keep their per-cell tagged `Scalar` form +(`OpDoc::Add { bind: BTreeMap }`, +crates/aura-cli/src/graph_construct.rs:37): a bind slot holds exactly one +value, so there the tag is the only kind carrier; an axis is a homogeneous +list, so the kind belongs to the axis. ### Validation, canonical form, introspection (secondary shapes) @@ -254,8 +262,10 @@ pub fn put_campaign(…) / get_campaign(…); // same pattern as pu pub fn validate_campaign_refs(doc: &CampaignDoc, roots: &StoreRoots) -> Vec; // RefFault: ProcessNotFound(id), StrategyNotFound(id), IdentityAmbiguityEmpty(id), -// AxisNotInParamSpace{strategy_ref, axis} — axes ⊆ the referenced blueprint's -// param_space (blueprint loaded from the store via blueprint_from_json). +// AxisNotInParamSpace{strategy_ref, axis}, AxisKindMismatch{strategy_ref, axis} +// — axes ⊆ the referenced blueprint's param_space, and each axis's declared +// kind must equal the param's ScalarKind (one check per axis; blueprint +// loaded from the store via blueprint_from_json). ``` Intrinsic P1 constraints enforced (the ratified ceiling made structural):