From 961a5e0df2bec16ea8586cde87f120a9a5969bb7 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Fri, 17 Jul 2026 05:11:52 +0100 Subject: [PATCH] Define explicit scalar input controls --- docs/input-capabilities.md | 8 +++++ .../dist/generated/input-capability.d.ts | 7 +++- .../eve-contracts/dist/generated/schemas.d.ts | 31 +++++++++++++++- .../eve-contracts/dist/generated/schemas.js | 35 +++++++++++++++++++ .../src/generated/input-capability.ts | 7 +++- .../eve-contracts/src/generated/schemas.ts | 35 +++++++++++++++++++ .../eve-contracts/test/contracts.test.mjs | 34 ++++++++++++++++++ ...mecult.eve.input_capability.v1.schema.json | 11 +++++- 8 files changed, 164 insertions(+), 4 deletions(-) diff --git a/docs/input-capabilities.md b/docs/input-capabilities.md index 54064f3..6d04671 100644 --- a/docs/input-capabilities.md +++ b/docs/input-capabilities.md @@ -21,6 +21,14 @@ send changed values through the action's advertised operation; providers own interpretation, persistence, receipts, and simulation. A client must not infer decay or repair a missed release by mutating presented state. +The `scalar.v1` model exposes an explicit finite numeric control rather than a +sampled device axis. `currentValue` is provider-owned state. `minimumValue`, +`maximumValue`, and `stepValue` are optional input constraints, and `unit` is an +optional semantic label. A client submits the chosen value to `payloadKey`; it +does not update `currentValue` optimistically or clamp values beyond the +advertised constraints. Providers may leave the range open when the original +control accepted arbitrary numeric input. + The `view-direction.v1` model is a one-shot value sampled when its gesture is performed. The client reads the active semantic view's normalized world-space forward direction and writes X, Y, and Z to the three advertised `payloadKeys` diff --git a/packages/eve-contracts/dist/generated/input-capability.d.ts b/packages/eve-contracts/dist/generated/input-capability.d.ts index dbc35b2..d289122 100644 --- a/packages/eve-contracts/dist/generated/input-capability.d.ts +++ b/packages/eve-contracts/dist/generated/input-capability.d.ts @@ -1,8 +1,13 @@ export type InputValue = ({ [k: string]: any; } & { - model: ("button-hold.v1" | "axis.v1" | "view-direction.v1"); + model: ("button-hold.v1" | "axis.v1" | "scalar.v1" | "view-direction.v1"); payloadKey?: string; + currentValue?: number; + minimumValue?: number; + maximumValue?: number; + stepValue?: number; + unit?: string; /** * @minItems 3 * @maxItems 3 diff --git a/packages/eve-contracts/dist/generated/schemas.d.ts b/packages/eve-contracts/dist/generated/schemas.d.ts index adc9641..accaeef 100644 --- a/packages/eve-contracts/dist/generated/schemas.d.ts +++ b/packages/eve-contracts/dist/generated/schemas.d.ts @@ -723,12 +723,29 @@ export declare const eveContractSchemas: { readonly required: readonly ["model"]; readonly properties: { readonly model: { - readonly enum: readonly ["button-hold.v1", "axis.v1", "view-direction.v1"]; + readonly enum: readonly ["button-hold.v1", "axis.v1", "scalar.v1", "view-direction.v1"]; }; readonly payloadKey: { readonly type: "string"; readonly minLength: 1; }; + readonly currentValue: { + readonly type: "number"; + }; + readonly minimumValue: { + readonly type: "number"; + }; + readonly maximumValue: { + readonly type: "number"; + }; + readonly stepValue: { + readonly type: "number"; + readonly exclusiveMinimum: 0; + }; + readonly unit: { + readonly type: "string"; + readonly minLength: 1; + }; readonly payloadKeys: { readonly type: "array"; readonly minItems: 3; @@ -755,6 +772,18 @@ export declare const eveContractSchemas: { readonly else: { readonly required: readonly ["payloadKey"]; }; + }, { + readonly if: { + readonly properties: { + readonly model: { + readonly const: "scalar.v1"; + }; + }; + readonly required: readonly ["model"]; + }; + readonly then: { + readonly required: readonly ["currentValue"]; + }; }]; readonly additionalProperties: false; }; diff --git a/packages/eve-contracts/dist/generated/schemas.js b/packages/eve-contracts/dist/generated/schemas.js index 2012f12..34e3784 100644 --- a/packages/eve-contracts/dist/generated/schemas.js +++ b/packages/eve-contracts/dist/generated/schemas.js @@ -874,6 +874,7 @@ export const eveContractSchemas = { "enum": [ "button-hold.v1", "axis.v1", + "scalar.v1", "view-direction.v1" ] }, @@ -881,6 +882,23 @@ export const eveContractSchemas = { "type": "string", "minLength": 1 }, + "currentValue": { + "type": "number" + }, + "minimumValue": { + "type": "number" + }, + "maximumValue": { + "type": "number" + }, + "stepValue": { + "type": "number", + "exclusiveMinimum": 0 + }, + "unit": { + "type": "string", + "minLength": 1 + }, "payloadKeys": { "type": "array", "minItems": 3, @@ -914,6 +932,23 @@ export const eveContractSchemas = { "payloadKey" ] } + }, + { + "if": { + "properties": { + "model": { + "const": "scalar.v1" + } + }, + "required": [ + "model" + ] + }, + "then": { + "required": [ + "currentValue" + ] + } } ], "additionalProperties": false diff --git a/packages/eve-contracts/src/generated/input-capability.ts b/packages/eve-contracts/src/generated/input-capability.ts index ea3f862..a056397 100644 --- a/packages/eve-contracts/src/generated/input-capability.ts +++ b/packages/eve-contracts/src/generated/input-capability.ts @@ -3,8 +3,13 @@ export type InputValue = ({ [k: string]: any } & { -model: ("button-hold.v1" | "axis.v1" | "view-direction.v1") +model: ("button-hold.v1" | "axis.v1" | "scalar.v1" | "view-direction.v1") payloadKey?: string +currentValue?: number +minimumValue?: number +maximumValue?: number +stepValue?: number +unit?: string /** * @minItems 3 * @maxItems 3 diff --git a/packages/eve-contracts/src/generated/schemas.ts b/packages/eve-contracts/src/generated/schemas.ts index ffbbca6..6ff01d1 100644 --- a/packages/eve-contracts/src/generated/schemas.ts +++ b/packages/eve-contracts/src/generated/schemas.ts @@ -874,6 +874,7 @@ export const eveContractSchemas = { "enum": [ "button-hold.v1", "axis.v1", + "scalar.v1", "view-direction.v1" ] }, @@ -881,6 +882,23 @@ export const eveContractSchemas = { "type": "string", "minLength": 1 }, + "currentValue": { + "type": "number" + }, + "minimumValue": { + "type": "number" + }, + "maximumValue": { + "type": "number" + }, + "stepValue": { + "type": "number", + "exclusiveMinimum": 0 + }, + "unit": { + "type": "string", + "minLength": 1 + }, "payloadKeys": { "type": "array", "minItems": 3, @@ -914,6 +932,23 @@ export const eveContractSchemas = { "payloadKey" ] } + }, + { + "if": { + "properties": { + "model": { + "const": "scalar.v1" + } + }, + "required": [ + "model" + ] + }, + "then": { + "required": [ + "currentValue" + ] + } } ], "additionalProperties": false diff --git a/packages/eve-contracts/test/contracts.test.mjs b/packages/eve-contracts/test/contracts.test.mjs index 01155f9..8f8facc 100644 --- a/packages/eve-contracts/test/contracts.test.mjs +++ b/packages/eve-contracts/test/contracts.test.mjs @@ -105,6 +105,30 @@ test("validates scalar and view-direction input value models", () => { inputValue: { model: "button-hold.v1", payloadKey: "active" }, }], }).actions[0].inputValue.payloadKey, "active"); + assert.deepEqual(parseEveInputCapability({ + ...base, + actions: [{ + actionId: "equipment.0.temperature", + label: "Target Temperature", + operation: "equipment.set-temperature", + availability: "available", + inputValue: { + model: "scalar.v1", + payloadKey: "scalarValue", + currentValue: 300, + minimumValue: 0, + stepValue: 1, + unit: "kelvin", + }, + }], + }).actions[0].inputValue, { + model: "scalar.v1", + payloadKey: "scalarValue", + currentValue: 300, + minimumValue: 0, + stepValue: 1, + unit: "kelvin", + }); assert.deepEqual(parseEveInputCapability({ ...base, actions: [{ @@ -128,6 +152,16 @@ test("validates scalar and view-direction input value models", () => { inputValue: { model: "view-direction.v1", payloadKey: "directionX" }, }], }), EveContractValidationError); + assert.throws(() => parseEveInputCapability({ + ...base, + actions: [{ + actionId: "equipment.0.temperature", + label: "Target Temperature", + operation: "equipment.set-temperature", + availability: "available", + inputValue: { model: "scalar.v1", payloadKey: "scalarValue" }, + }], + }), EveContractValidationError); }); test("validates an existing canonical Eve surface fixture", async () => { diff --git a/schemas/gamecult.eve.input_capability.v1.schema.json b/schemas/gamecult.eve.input_capability.v1.schema.json index 629a8b8..d0135c1 100644 --- a/schemas/gamecult.eve.input_capability.v1.schema.json +++ b/schemas/gamecult.eve.input_capability.v1.schema.json @@ -48,8 +48,13 @@ "type": "object", "required": ["model"], "properties": { - "model": { "enum": ["button-hold.v1", "axis.v1", "view-direction.v1"] }, + "model": { "enum": ["button-hold.v1", "axis.v1", "scalar.v1", "view-direction.v1"] }, "payloadKey": { "type": "string", "minLength": 1 }, + "currentValue": { "type": "number" }, + "minimumValue": { "type": "number" }, + "maximumValue": { "type": "number" }, + "stepValue": { "type": "number", "exclusiveMinimum": 0 }, + "unit": { "type": "string", "minLength": 1 }, "payloadKeys": { "type": "array", "minItems": 3, @@ -63,6 +68,10 @@ "if": { "properties": { "model": { "const": "view-direction.v1" } }, "required": ["model"] }, "then": { "required": ["payloadKeys"] }, "else": { "required": ["payloadKey"] } + }, + { + "if": { "properties": { "model": { "const": "scalar.v1" } }, "required": ["model"] }, + "then": { "required": ["currentValue"] } } ], "additionalProperties": false