From 4afa9c3632c5138104746b87e8091d2c179d6f3e Mon Sep 17 00:00:00 2001 From: Seth Fitzsimmons Date: Wed, 26 Aug 2026 10:37:57 -0700 Subject: [PATCH 1/2] feat(schema)!: remove the non-null defaults from level, land and water Implements the policy in #695 for three of the four remaining sites: no schema field carries a non-null default, and where a value would come from a default the publisher supplies it in the published data. - `Stacked.level` drops `= 0` and becomes `None`. The absence sentence goes on the field rather than the `Level` NewType, because `Level` is also the type of the required `LevelRule.value`, where absence is impossible. Removing the default also retires the `# type: ignore[assignment]` that existed only to admit it. - `Land.class_`, `Land.subtype`, `Water.class_`, `Water.subtype` become required, matching `Infrastructure` and `LandUse`. No consumer sees a difference. Measured on release 2026-08-19.0, one partition per type: `level = 0` appears in 0 of 1,174,629 land and 0 of 1,985,644 water rows, and `class`/`subtype` are never null in either. Both partitions validate clean against the changed models. `SpeedLimitRule.is_max_speed_variable` is deliberately left out. Its default cannot be removed without stating what absence means, and the data does not settle that: the publisher writes an explicit `false` for closed-world booleans (`building.has_parts` is 0 null / 4,777,998 false) but never for this one (0 false in 1,376,379 rules across three partitions), so absence is an unrecorded value rather than a recorded "no". That call needs a domain owner. Refs #696 Signed-off-by: Seth Fitzsimmons --- .../changelog.d/696.breaking.md | 1 + .../src/overture/schema/common/level.py | 10 +++++++++- .../changelog.d/696.breaking.md | 1 + .../src/overture/schema/base/land.py | 6 ++---- .../src/overture/schema/base/water.py | 15 ++------------- .../tests/infrastructure_baseline_schema.json | 3 +-- .../tests/land_baseline_schema.json | 13 ++++++------- .../tests/land_use_baseline_schema.json | 3 +-- .../tests/water_baseline_schema.json | 13 ++++++------- .../changelog.d/696.breaking.md | 1 + .../tests/building_baseline_schema.json | 3 +-- .../tests/building_part_baseline_schema.json | 3 +-- 12 files changed, 32 insertions(+), 40 deletions(-) create mode 100644 packages/overture-schema-common/changelog.d/696.breaking.md create mode 100644 packages/overture-schema-theme-base/changelog.d/696.breaking.md create mode 100644 packages/overture-schema-theme-buildings/changelog.d/696.breaking.md diff --git a/packages/overture-schema-common/changelog.d/696.breaking.md b/packages/overture-schema-common/changelog.d/696.breaking.md new file mode 100644 index 000000000..d8f651e92 --- /dev/null +++ b/packages/overture-schema-common/changelog.d/696.breaking.md @@ -0,0 +1 @@ +Removed the `level` default of `0`; a feature parsed without a level now yields `None` instead of `0`, and the field description states that absence means visual level. diff --git a/packages/overture-schema-common/src/overture/schema/common/level.py b/packages/overture-schema-common/src/overture/schema/common/level.py index 14ea6f15d..6073302ad 100644 --- a/packages/overture-schema-common/src/overture/schema/common/level.py +++ b/packages/overture-schema-common/src/overture/schema/common/level.py @@ -20,4 +20,12 @@ class Stacked(BaseModel): """Properties defining feature Z-order, i.e., stacking order.""" - level: Level | None = 0 # type: ignore[assignment] + level: Annotated[ + Level | None, + Field( + description=( + "Z-order of the feature where 0 is visual level. A feature " + "without a level is at visual level." + ) + ), + ] = None diff --git a/packages/overture-schema-theme-base/changelog.d/696.breaking.md b/packages/overture-schema-theme-base/changelog.d/696.breaking.md new file mode 100644 index 000000000..d12a80821 --- /dev/null +++ b/packages/overture-schema-theme-base/changelog.d/696.breaking.md @@ -0,0 +1 @@ +Made `class` and `subtype` required on `Land` and `Water`, matching `Infrastructure` and `LandUse`; constructing either without them now raises instead of falling back to `land`/`water`. diff --git a/packages/overture-schema-theme-base/src/overture/schema/base/land.py b/packages/overture-schema-theme-base/src/overture/schema/base/land.py index d3119912e..4001eb075 100644 --- a/packages/overture-schema-theme-base/src/overture/schema/base/land.py +++ b/packages/overture-schema-theme-base/src/overture/schema/base/land.py @@ -139,10 +139,8 @@ class Land( # Required - class_: Annotated[LandClass, Field(default=LandClass.LAND, alias="class")] = ( - LandClass.LAND - ) - subtype: Annotated[LandSubtype, Field(default=LandSubtype.LAND)] = LandSubtype.LAND + class_: Annotated[LandClass, Field(alias="class")] + subtype: LandSubtype # Optional diff --git a/packages/overture-schema-theme-base/src/overture/schema/base/water.py b/packages/overture-schema-theme-base/src/overture/schema/base/water.py index fbf5ab4b9..37978a376 100644 --- a/packages/overture-schema-theme-base/src/overture/schema/base/water.py +++ b/packages/overture-schema-theme-base/src/overture/schema/base/water.py @@ -138,19 +138,8 @@ class Water( # Required - class_: Annotated[ - WaterClass, - Field( - default=WaterClass.WATER, - alias="class", - ), - ] = WaterClass.WATER - subtype: Annotated[ - WaterSubtype, - Field( - default=WaterSubtype.WATER, - ), - ] = WaterSubtype.WATER + class_: Annotated[WaterClass, Field(alias="class")] + subtype: WaterSubtype # Optional diff --git a/packages/overture-schema-theme-base/tests/infrastructure_baseline_schema.json b/packages/overture-schema-theme-base/tests/infrastructure_baseline_schema.json index 1c3336a31..b93b440bb 100644 --- a/packages/overture-schema-theme-base/tests/infrastructure_baseline_schema.json +++ b/packages/overture-schema-theme-base/tests/infrastructure_baseline_schema.json @@ -643,8 +643,7 @@ "type": "number" }, "level": { - "default": 0, - "description": "Z-order of the feature where 0 is visual level", + "description": "Z-order of the feature where 0 is visual level. A feature without a level is at visual level.", "maximum": 2147483647, "minimum": -2147483648, "title": "Level", diff --git a/packages/overture-schema-theme-base/tests/land_baseline_schema.json b/packages/overture-schema-theme-base/tests/land_baseline_schema.json index f41908d7c..7ff091acf 100644 --- a/packages/overture-schema-theme-base/tests/land_baseline_schema.json +++ b/packages/overture-schema-theme-base/tests/land_baseline_schema.json @@ -507,8 +507,7 @@ }, "properties": { "class": { - "$ref": "#/$defs/LandClass", - "default": "land" + "$ref": "#/$defs/LandClass" }, "elevation": { "description": "Elevation above sea level of the feature in meters.", @@ -518,8 +517,7 @@ "type": "integer" }, "level": { - "default": 0, - "description": "Z-order of the feature where 0 is visual level", + "description": "Z-order of the feature where 0 is visual level. A feature without a level is at visual level.", "maximum": 2147483647, "minimum": -2147483648, "title": "Level", @@ -546,8 +544,7 @@ "uniqueItems": true }, "subtype": { - "$ref": "#/$defs/LandSubtype", - "default": "land" + "$ref": "#/$defs/LandSubtype" }, "surface": { "$ref": "#/$defs/SurfaceMaterial" @@ -579,7 +576,9 @@ "required": [ "theme", "type", - "version" + "version", + "class", + "subtype" ], "type": "object" }, diff --git a/packages/overture-schema-theme-base/tests/land_use_baseline_schema.json b/packages/overture-schema-theme-base/tests/land_use_baseline_schema.json index e85b38189..440b2b9ca 100644 --- a/packages/overture-schema-theme-base/tests/land_use_baseline_schema.json +++ b/packages/overture-schema-theme-base/tests/land_use_baseline_schema.json @@ -595,8 +595,7 @@ "type": "integer" }, "level": { - "default": 0, - "description": "Z-order of the feature where 0 is visual level", + "description": "Z-order of the feature where 0 is visual level. A feature without a level is at visual level.", "maximum": 2147483647, "minimum": -2147483648, "title": "Level", diff --git a/packages/overture-schema-theme-base/tests/water_baseline_schema.json b/packages/overture-schema-theme-base/tests/water_baseline_schema.json index 40ca4333b..8434a41bb 100644 --- a/packages/overture-schema-theme-base/tests/water_baseline_schema.json +++ b/packages/overture-schema-theme-base/tests/water_baseline_schema.json @@ -468,8 +468,7 @@ }, "properties": { "class": { - "$ref": "#/$defs/WaterClass", - "default": "water" + "$ref": "#/$defs/WaterClass" }, "is_intermittent": { "description": "Whether the water body exists intermittently, not permanently", @@ -482,8 +481,7 @@ "type": "boolean" }, "level": { - "default": 0, - "description": "Z-order of the feature where 0 is visual level", + "description": "Z-order of the feature where 0 is visual level. A feature without a level is at visual level.", "maximum": 2147483647, "minimum": -2147483648, "title": "Level", @@ -510,8 +508,7 @@ "uniqueItems": true }, "subtype": { - "$ref": "#/$defs/WaterSubtype", - "default": "water" + "$ref": "#/$defs/WaterSubtype" }, "theme": { "const": "base", @@ -540,7 +537,9 @@ "required": [ "theme", "type", - "version" + "version", + "class", + "subtype" ], "type": "object" }, diff --git a/packages/overture-schema-theme-buildings/changelog.d/696.breaking.md b/packages/overture-schema-theme-buildings/changelog.d/696.breaking.md new file mode 100644 index 000000000..481c64d60 --- /dev/null +++ b/packages/overture-schema-theme-buildings/changelog.d/696.breaking.md @@ -0,0 +1 @@ +Buildings and building parts parsed without a `level` now yield `None` instead of `0`, following the removal of the `Stacked.level` default. diff --git a/packages/overture-schema-theme-buildings/tests/building_baseline_schema.json b/packages/overture-schema-theme-buildings/tests/building_baseline_schema.json index 1f629b551..2bc134246 100644 --- a/packages/overture-schema-theme-buildings/tests/building_baseline_schema.json +++ b/packages/overture-schema-theme-buildings/tests/building_baseline_schema.json @@ -561,8 +561,7 @@ "type": "boolean" }, "level": { - "default": 0, - "description": "Z-order of the feature where 0 is visual level", + "description": "Z-order of the feature where 0 is visual level. A feature without a level is at visual level.", "maximum": 2147483647, "minimum": -2147483648, "title": "Level", diff --git a/packages/overture-schema-theme-buildings/tests/building_part_baseline_schema.json b/packages/overture-schema-theme-buildings/tests/building_part_baseline_schema.json index 6617c368d..f511e30ec 100644 --- a/packages/overture-schema-theme-buildings/tests/building_part_baseline_schema.json +++ b/packages/overture-schema-theme-buildings/tests/building_part_baseline_schema.json @@ -444,8 +444,7 @@ "type": "boolean" }, "level": { - "default": 0, - "description": "Z-order of the feature where 0 is visual level", + "description": "Z-order of the feature where 0 is visual level. A feature without a level is at visual level.", "maximum": 2147483647, "minimum": -2147483648, "title": "Level", From df65004cf85569dc75d940707208efd2f231812d Mon Sep 17 00:00:00 2001 From: Seth Fitzsimmons Date: Wed, 26 Aug 2026 10:48:06 -0700 Subject: [PATCH 2/2] feat(schema)!: remove the is_max_speed_variable default Completes the removal begun in the previous commit: `SpeedLimitRule. is_max_speed_variable` drops `= False`. A rule parsed without the flag now yields `None` instead of `False`. The description is left as it stands. What absence means here is a domain question the schema should not answer by inference, and the existing wording does not define the term well enough to extend safely. Both are worth settling, separately, with the transportation folks. Refs #696 Signed-off-by: Seth Fitzsimmons --- .../changelog.d/696.breaking.md | 1 + .../src/overture/schema/transportation/segment/_common.py | 2 +- .../tests/segment_baseline_schema.json | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 packages/overture-schema-theme-transportation/changelog.d/696.breaking.md diff --git a/packages/overture-schema-theme-transportation/changelog.d/696.breaking.md b/packages/overture-schema-theme-transportation/changelog.d/696.breaking.md new file mode 100644 index 000000000..97a56be8d --- /dev/null +++ b/packages/overture-schema-theme-transportation/changelog.d/696.breaking.md @@ -0,0 +1 @@ +Removed the `is_max_speed_variable` default of `False`; a rule parsed without the flag now yields `None` instead of `False`. diff --git a/packages/overture-schema-theme-transportation/src/overture/schema/transportation/segment/_common.py b/packages/overture-schema-theme-transportation/src/overture/schema/transportation/segment/_common.py index 03c27bbae..c922e7d9c 100644 --- a/packages/overture-schema-theme-transportation/src/overture/schema/transportation/segment/_common.py +++ b/packages/overture-schema-theme-transportation/src/overture/schema/transportation/segment/_common.py @@ -322,7 +322,7 @@ class SpeedLimitRule(BaseModel): description="Indicates a variable speed corridor", strict=True, ), - ] = False + ] = None SpeedLimits = NewType( diff --git a/packages/overture-schema-theme-transportation/tests/segment_baseline_schema.json b/packages/overture-schema-theme-transportation/tests/segment_baseline_schema.json index 3bcceda22..d7473d5ed 100644 --- a/packages/overture-schema-theme-transportation/tests/segment_baseline_schema.json +++ b/packages/overture-schema-theme-transportation/tests/segment_baseline_schema.json @@ -1323,7 +1323,6 @@ "type": "array" }, "is_max_speed_variable": { - "default": false, "description": "Indicates a variable speed corridor", "title": "Is Max Speed Variable", "type": "boolean"