From ad41bed8b8c05720bcf8966d7d41cc80919874a3 Mon Sep 17 00:00:00 2001 From: Brummel Date: Fri, 3 Jul 2026 13:12:13 +0200 Subject: [PATCH] =?UTF-8?q?spec:=200106=20fix=20=E2=80=94=20axis=20declare?= =?UTF-8?q?s=20its=20kind=20once,=20values=20are=20bare=20JSON?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User veto (2026-07-03) of the per-cell tagged Scalar draft: the kind is a property of the parameter, not of each value. An axis is now {"kind": "I64", "values": [8, 12, 16]} — mixed-kind axes are unrepresentable, canonical form normalizes value spelling, and the referential kind check is one comparison per axis. Op-script binds keep their tagged form (single-value slots). Decision log: #189. refs #189 --- .../0106-research-artifact-vocabularies.md | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) 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):