You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Our OpenAPI drift detection is one-directional for fields (spec → code). It catches fields added to the spec that are missing from models.rs, and optionality flips (T ↔ Option<T>) on fields present in both. It does not catch the reverse: a field that has been removed from the live spec but still lingers as a struct field in models.rs.
This is exactly how #235 / #236 slipped through — storageSize was dropped from the Postgres request schemas (PostgresServicePostRequest, PostgresServicePatchRequest, BasePostgresService, PostgresServiceListItem) upstream, the vendored snapshot was even refreshed to match, but models.rs kept the field and every check stayed green. A model that is a superset of the spec passes all current field checks.
Root cause
Every field check iterates over spec properties only:
scripts/check-openapi-drift.py
check_missing_fields (~L415): for prop_name in props: if prop_name not in fields → missing
check_field_optionality (~L331): same loop; a field in the model but not the spec hits if prop_name not in fields: continue (silently skipped)
struct_fields_cover_every_spec_property and field_optionality_matches_spec — same spec → struct direction
Telling asymmetry
For operations the check is already bidirectional: assert_client_operation_coverage (spec_coverage_test.rs:80) reports both missingandextras ("Extra client methods (not in spec)"). The same "extras" concept was simply never built for fields.
Proposed fix
Add an "extra struct fields" check mirroring the existing "extra client methods" one:
In check-openapi-drift.py: a reverse pass that, for each struct mapped to a spec schema, flags model fields with no corresponding spec property. Surface it in the drift report (new "Extra struct fields (not in spec)" section + summary row).
In spec_coverage_test.rs: a struct_fields_have_no_extras_vs_spec test asserting no model field is absent from its spec schema.
Wrinkle to handle: allowlist
Some struct fields legitimately have no spec property, e.g.:
Response-only / computed fields we add intentionally.
Structs in models.rs that don't map 1:1 to a spec schema (the checks already continue when the schema isn't found — keep that).
Introduce an allowlist constant analogous to OPTIONALITY_EXEMPTIONS (e.g. EXTRA_FIELD_EXEMPTIONS of ("RustStructName", "fieldName")) so intentional additions don't false-positive. The drift script should parse the same list (it already does this for exemptions via parse_optionality_exemptions) so the report and the test stay in sync.
Acceptance criteria
Removing a field from a spec schema while leaving it in models.rs fails spec_coverage_test.rs and shows up in the drift report.
Intentional code-only fields are covered by a documented allowlist that fails when it goes stale.
Problem
Our OpenAPI drift detection is one-directional for fields (spec → code). It catches fields added to the spec that are missing from
models.rs, and optionality flips (T↔Option<T>) on fields present in both. It does not catch the reverse: a field that has been removed from the live spec but still lingers as a struct field inmodels.rs.This is exactly how #235 / #236 slipped through —
storageSizewas dropped from the Postgres request schemas (PostgresServicePostRequest,PostgresServicePatchRequest,BasePostgresService,PostgresServiceListItem) upstream, the vendored snapshot was even refreshed to match, butmodels.rskept the field and every check stayed green. A model that is a superset of the spec passes all current field checks.Root cause
Every field check iterates over spec properties only:
scripts/check-openapi-drift.pycheck_missing_fields(~L415):for prop_name in props: if prop_name not in fields → missingcheck_field_optionality(~L331): same loop; a field in the model but not the spec hitsif prop_name not in fields: continue(silently skipped)crates/clickhouse-cloud-api/tests/spec_coverage_test.rsstruct_fields_cover_every_spec_propertyandfield_optionality_matches_spec— same spec → struct directionTelling asymmetry
For operations the check is already bidirectional:
assert_client_operation_coverage(spec_coverage_test.rs:80) reports bothmissingandextras("Extra client methods (not in spec)"). The same "extras" concept was simply never built for fields.Proposed fix
Add an "extra struct fields" check mirroring the existing "extra client methods" one:
check-openapi-drift.py: a reverse pass that, for each struct mapped to a spec schema, flags model fields with no corresponding spec property. Surface it in the drift report (new "Extra struct fields (not in spec)" section + summary row).spec_coverage_test.rs: astruct_fields_have_no_extras_vs_spectest asserting no model field is absent from its spec schema.Wrinkle to handle: allowlist
Some struct fields legitimately have no spec property, e.g.:
models.rsthat don't map 1:1 to a spec schema (the checks alreadycontinuewhen the schema isn't found — keep that).Introduce an allowlist constant analogous to
OPTIONALITY_EXEMPTIONS(e.g.EXTRA_FIELD_EXEMPTIONSof("RustStructName", "fieldName")) so intentional additions don't false-positive. The drift script should parse the same list (it already does this for exemptions viaparse_optionality_exemptions) so the report and the test stay in sync.Acceptance criteria
models.rsfailsspec_coverage_test.rsand shows up in the drift report.storageSizefields would have been flagged.