Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 19 additions & 11 deletions SCHEMA.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ This schema set governs six JSON Schema Draft 2020-12 resources:
|---|---|
| `routines/schemas/common.schema.json` | `https://open-control-library.example/schemas/routine-common-v1.json` |
| `routines/schemas/class-manifest.schema.json` | `https://open-control-library.example/schemas/routine-class-manifest-v1.json` |
| `routines/schemas/interface.schema.json` | `https://open-control-library.example/schemas/routine-interface-v2.json` |
| `routines/schemas/interface.schema.json` | `https://open-control-library.example/schemas/routine-interface-v3.json` |
| `routines/schemas/specialization.schema.json` | `https://open-control-library.example/schemas/routine-specialization-v1.json` |
| `routines/schemas/routine-semantic-profile.schema.json` | `https://open-control-library.example/schemas/routine-semantic-profile-v1.json` |
| `routines/schemas/routine-derivation-manifest.schema.json` | `https://open-control-library.example/schemas/routine-derivation-manifest-v1.json` |
Expand All @@ -112,8 +112,8 @@ Each resource declares
fragments or the six absolute IDs above and resolve from an in-memory registry.
Validation performs no network or filesystem retrieval for schema references.
Objects are closed unless stated otherwise. Semantic-only definitions belong to
the semantic-profile resource; `routine-common-v1` remains the existing routine
class/interface contract.
the semantic-profile resource. `routine-common-v1` retains its unchanged reusable
definitions; the interface v3 resource defines its revised dimension form locally.

Canonical class IDs have the form
`G36-05-(01..22)-<UPPERCASE-HYPHENATED-CLASS-SLUG>`. Scope IDs are invalid
Expand Down Expand Up @@ -152,7 +152,7 @@ share one non-root class directory and end in `interface.json`,
`specialization.schema.json`, and `specialization.json`, respectively. This is
an artifact-location contract, not a production source-to-class mapping.

#### Interfaces (`cxf-library/routine-interface/v2`)
#### Interfaces (`cxf-library/routine-interface/v3`)

An interface has exactly `schema`, `canonical_id`, `revision`, `types`,
`dimensions`, `parameters`, and `connectors`. Types and enums are local to that
Expand All @@ -167,9 +167,15 @@ values use the stable member IDs; no integer lowering code is assigned.

A type use is either primitive or a reference to a local named type. A shape is
either scalar or an array with an ordered list of one or two dimension IDs.
Dimensions have unique IDs. Their extent is either a positive fixed integer or
a reference to a scalar Integer parameter. Rank greater than two, zero extents,
ragged matrices, and arithmetic dimension expressions are invalid.
Dimensions have unique IDs. A fixed dimension declares a positive integer
extent and a nonempty ordered `members` list whose count MUST equal that extent.
A parameter-driven dimension references a scalar Integer parameter and MUST NOT
declare canonical members; `routine-specialization/v1` owns its member list.
Dimension members are authored stable identities, not identities derived from
array ordinals. Authored list order is contract order. Within one interface and
specialization pair, member IDs MUST be unique across every fixed and
parameter-driven dimension. Rank greater than two, zero extents, ragged
matrices, and arithmetic dimension expressions are invalid.

Parameters have unique IDs, a type use, shape, `fixed` or `configurable`
configurability, an optional typed default, and optional numeric minimum and
Expand All @@ -192,15 +198,17 @@ or resolve optional branches.
A specialization input has exactly `schema`, `canonical_id`, `revision`,
`parameters`, and `members`. `parameters` is an ordered list of unique parameter
IDs and JSON values. `members` binds each parameter-driven dimension ID to a
nonempty ordered list of globally unique stable member IDs.
nonempty ordered list of stable member IDs. Fixed dimensions are invalid targets.
These IDs MUST be unique across the specialization's member records and against
the canonical members of every fixed dimension in the paired interface.

The interface and specialization canonical ID and revision MUST agree with the
class manifest. Specialization checks parameter existence, fixed-parameter
override rejection, required configurable assignments, primitive and enum
value compatibility, numeric bounds, concrete dimension extents, rectangular
rank-one and rank-two values, and stable-member count. All numeric values MUST
be finite. A parameter-driven dimension resolves only from a positive Integer
effective value.
rank-one and rank-two values, and parameter-driven member count. All numeric
values MUST be finite. A parameter-driven dimension resolves only from a
positive Integer effective value, and its member count MUST equal that value.

Specialization is input only. It contains no connector bindings, point IDs,
resolved connector set, source map, generated CXF, runtime state, engine
Expand Down
13 changes: 9 additions & 4 deletions routines/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,15 @@ cargo run --manifest-path tools/verify/Cargo.toml -- --routines
Canonical IDs name parameterized engineering classes, never fixed parameter
variants or source locations. Local types and enums belong to one interface.
The schemas cover scalar and rank-one/rank-two typed values,
parameter-controlled dimensions, stable repeated-member IDs, and
parameter-only optional-connector guards. They do not evaluate guards or
define production connector bindings, source mapping instances,
specializations, generated deployments, or executable CXF.
fixed and parameter-controlled dimensions, stable repeated-member IDs, and
parameter-only optional-connector guards. A fixed dimension owns an ordered
canonical member list in interface v3 whose count equals its extent. A
parameter-driven dimension has no canonical member list; specialization v1 owns
its ordered members. Member IDs are authored stable identities rather than
array ordinals and are unique across all dimensions in an interface and
specialization pair. The schemas do not evaluate guards or define production
connector bindings, source mapping instances, specializations, generated
deployments, or executable CXF.

The semantic and derivation schemas are exercised only by synthetic fixtures
under `tools/lint/tests/fixtures/routine_semantics/`. Validation is local and
Expand Down
59 changes: 56 additions & 3 deletions routines/schemas/interface.schema.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,65 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://open-control-library.example/schemas/routine-interface-v2.json",
"$id": "https://open-control-library.example/schemas/routine-interface-v3.json",
"title": "Open Control Library canonical routine interface",
"$defs": {
"dimension": {
"oneOf": [
{
"type": "object",
"additionalProperties": false,
"required": ["id", "extent", "members"],
"properties": {
"id": {
"$ref": "https://open-control-library.example/schemas/routine-common-v1.json#/$defs/localId"
},
"extent": {
"type": "object",
"additionalProperties": false,
"required": ["kind", "value"],
"properties": {
"kind": {"const": "fixed"},
"value": {"type": "integer", "minimum": 1}
}
},
"members": {
"type": "array",
"minItems": 1,
"items": {
"$ref": "https://open-control-library.example/schemas/routine-common-v1.json#/$defs/stableMemberId"
}
}
}
},
{
"type": "object",
"additionalProperties": false,
"required": ["id", "extent"],
"properties": {
"id": {
"$ref": "https://open-control-library.example/schemas/routine-common-v1.json#/$defs/localId"
},
"extent": {
"type": "object",
"additionalProperties": false,
"required": ["kind", "parameter"],
"properties": {
"kind": {"const": "parameter"},
"parameter": {
"$ref": "https://open-control-library.example/schemas/routine-common-v1.json#/$defs/localId"
}
}
}
}
}
]
}
},
"type": "object",
"additionalProperties": false,
"required": ["schema", "canonical_id", "revision", "types", "dimensions", "parameters", "connectors"],
"properties": {
"schema": {"const": "cxf-library/routine-interface/v2"},
"schema": {"const": "cxf-library/routine-interface/v3"},
"canonical_id": {
"$ref": "https://open-control-library.example/schemas/routine-common-v1.json#/$defs/canonicalId"
},
Expand All @@ -22,7 +75,7 @@
"dimensions": {
"type": "array",
"items": {
"$ref": "https://open-control-library.example/schemas/routine-common-v1.json#/$defs/dimension"
"$ref": "#/$defs/dimension"
}
},
"parameters": {
Expand Down
28 changes: 22 additions & 6 deletions tools/lint/routine_schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
CLASS_MANIFEST_ID = (
"https://open-control-library.example/schemas/routine-class-manifest-v1.json"
)
INTERFACE_ID = "https://open-control-library.example/schemas/routine-interface-v2.json"
INTERFACE_ID = "https://open-control-library.example/schemas/routine-interface-v3.json"
SPECIALIZATION_ID = (
"https://open-control-library.example/schemas/routine-specialization-v1.json"
)
Expand Down Expand Up @@ -650,9 +650,24 @@ def _check_interface_and_specialization(interface, specialization, errors):
f"{interface_label}: $.parameters[{index}].constraints minimum exceeds maximum"
)

all_member_locations = {}
for index, dimension in enumerate(interface["dimensions"]):
extent = dimension["extent"]
if extent["kind"] != "parameter":
if extent["kind"] == "fixed":
members = dimension["members"]
if len(members) != extent["value"]:
errors.append(
f"{interface_label}: $.dimensions[{index}].members: expected {extent['value']} members, found {len(members)}"
)
for member_index, member_id in enumerate(members):
location = f"{interface_label}: $.dimensions[{index}].members[{member_index}]"
first_location = all_member_locations.get(member_id)
if first_location is not None:
errors.append(
f"{location}: duplicate stable member {member_id!r}; first used at {first_location}"
)
else:
all_member_locations[member_id] = location
continue
parameter_id = extent["parameter"]
parameter = parameters.get(parameter_id)
Expand Down Expand Up @@ -789,7 +804,6 @@ def _check_interface_and_specialization(interface, specialization, errors):
errors.append(
f"{specialization_label}: parameter-driven dimension {missing!r} requires stable members"
)
all_member_ids = {}
for index, record in enumerate(specialization["members"]):
dimension_id = record["dimension"]
dimension = dimensions.get(dimension_id)
Expand All @@ -807,12 +821,14 @@ def _check_interface_and_specialization(interface, specialization, errors):
f"{specialization_label}: $.members[{index}].members: expected {expected} members, found {len(record['members'])}"
)
for member_index, member_id in enumerate(record["members"]):
if member_id in all_member_ids:
location = f"{specialization_label}: $.members[{index}].members[{member_index}]"
first_location = all_member_locations.get(member_id)
if first_location is not None:
errors.append(
f"{specialization_label}: $.members[{index}].members[{member_index}]: duplicate stable member {member_id!r}"
f"{location}: duplicate stable member {member_id!r}; first used at {first_location}"
)
else:
all_member_ids[member_id] = (index, member_index)
all_member_locations[member_id] = location


def _check_cross_document(manifest, interface, specialization, errors):
Expand Down
5 changes: 3 additions & 2 deletions tools/lint/tests/fixtures/routine_schemas/interface.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"schema": "cxf-library/routine-interface/v2",
"schema": "cxf-library/routine-interface/v3",
"canonical_id": "G36-05-16-SYNTHETIC-SCHEMA-TEST",
"revision": 1,
"types": [
Expand Down Expand Up @@ -31,7 +31,8 @@
"dimensions": [
{
"id": "fixed_pair",
"extent": {"kind": "fixed", "value": 2}
"extent": {"kind": "fixed", "value": 2},
"members": ["primary", "secondary"]
},
{
"id": "zones",
Expand Down
Loading
Loading