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
Follow-up to #275 / #290, which added enum-value drift detection for models.rs. The CLI has the same class of problem one layer up.
Problem
The CLI keeps eight hand-maintained copies of spec enum values for client-side validation (necessary because the library enums' untagged Unknown(String) catch-all means serde never fails):
postgres.rs: KNOWN_PG_PROVIDERS, KNOWN_PG_VERSIONS, KNOWN_PG_HA_TYPES (the latter two also feed clap PossibleValuesParser, i.e. --help output)
Nothing guards any of them — the same silent-rot mode #275 fixed for models.rs. It is already happening: KNOWN_REGIONS is missing europe-west2 (added to the library in #290) andca-central-1 (added in #298, i.e. the list rotted again while this issue was open). parse_serde_enum hard-rejects unknown values, so chctl cloud service create --region europe-west2 fails client-side even though the API accepts it.
We will not patch the lists by hand — that just resets the clock. Fix the mechanism instead.
Fix
Since #290 the library enums are guaranteed in lockstep with the spec, so derive the CLI's value lists from the library and have the analyzer enforce the new surface:
1. Library: pub const VALUES on the value enums the CLI consumes
In models.rs, add to each enum the CLI validates against (ServicePostRequestProvider, ServicePostRequestRegion, ServicePostRequestReleasechannel, ServicePostRequestCompliancetype, ServicePostRequestProfile, PgProvider, PgVersion, PgHaType):
implServicePostRequestRegion{/// Wire values accepted by the API, excluding the catch-all.pubconstVALUES:&'static[&'staticstr] = &["ap-northeast-1",/* … */];}
Hand-written literal slices, not a proc macro or strum: wire values come from #[serde(rename)], not variant names, and this repo's approach (explicit serde literals, rename_all rejected) depends on greppable literals syn can parse. A macro-generated const would be invisible to the analyzer.
2. Analyzer: verify VALUES against the enum's wire values
VALUES is another copy of the literals — acceptable only because it is machine-checked. The analyzer already parses every enum's variants, serde renames, and catch-alls with syn. Add a typed FindingKind: any enum that declares a VALUES const must have it exactly equal to its non-catch-all wire values. Opt-in by construction ("if a VALUES const exists, it must match"), so the remaining enums need no change. Per the extension policy: focused inventory/comparison fixtures, deterministic JSON/text coverage, report schema_version bump, Python issue rendering; spec_coverage_test.rs stays a thin consumer.
This closes the chain: spec ↔ enum variants (guarded since #290) and enum variants ↔ VALUES (new check), so VALUES is transitively spec-accurate.
3. CLI: delete the KNOWN_* constants
parse_serde_enum::<ServicePostRequestRegion>(value, "region", ServicePostRequestRegion::VALUES) — error messages ("expected one of …") now sourced from the library.
Clap gets the same source: PossibleValuesParser::new(PgVersion::VALUES), so --help for the Postgres flags updates automatically.
Populating the new VALUES consts from the current enums fixes the europe-west2 / ca-central-1 gaps as a side effect.
Result
A spec drift remediation that adds an enum value flows automatically: spec → analyzer finding → models.rs variant + VALUES (analyzer fails CI if VALUES is not updated) → CLI validation, error messages, and --help update with zero CLI edits.
Out of scope / already fine
pg create --size deliberately takes a free-form String ("Server validates — accepts any value"), so PgSize needs no CLI change or VALUES const.
Follow-up to #275 / #290, which added enum-value drift detection for
models.rs. The CLI has the same class of problem one layer up.Problem
The CLI keeps eight hand-maintained copies of spec enum values for client-side validation (necessary because the library enums' untagged
Unknown(String)catch-all means serde never fails):commands.rs:KNOWN_PROVIDERS,KNOWN_REGIONS,KNOWN_RELEASE_CHANNELS,KNOWN_COMPLIANCE_TYPES,KNOWN_PROFILESpostgres.rs:KNOWN_PG_PROVIDERS,KNOWN_PG_VERSIONS,KNOWN_PG_HA_TYPES(the latter two also feed clapPossibleValuesParser, i.e.--helpoutput)Nothing guards any of them — the same silent-rot mode #275 fixed for
models.rs. It is already happening:KNOWN_REGIONSis missingeurope-west2(added to the library in #290) andca-central-1(added in #298, i.e. the list rotted again while this issue was open).parse_serde_enumhard-rejects unknown values, sochctl cloud service create --region europe-west2fails client-side even though the API accepts it.We will not patch the lists by hand — that just resets the clock. Fix the mechanism instead.
Fix
Since #290 the library enums are guaranteed in lockstep with the spec, so derive the CLI's value lists from the library and have the analyzer enforce the new surface:
1. Library:
pub const VALUESon the value enums the CLI consumesIn
models.rs, add to each enum the CLI validates against (ServicePostRequestProvider,ServicePostRequestRegion,ServicePostRequestReleasechannel,ServicePostRequestCompliancetype,ServicePostRequestProfile,PgProvider,PgVersion,PgHaType):Hand-written literal slices, not a proc macro or
strum: wire values come from#[serde(rename)], not variant names, and this repo's approach (explicit serde literals,rename_allrejected) depends on greppable literalssyncan parse. A macro-generated const would be invisible to the analyzer.2. Analyzer: verify
VALUESagainst the enum's wire valuesVALUESis another copy of the literals — acceptable only because it is machine-checked. The analyzer already parses every enum's variants, serde renames, and catch-alls withsyn. Add a typedFindingKind: any enum that declares aVALUESconst must have it exactly equal to its non-catch-all wire values. Opt-in by construction ("if aVALUESconst exists, it must match"), so the remaining enums need no change. Per the extension policy: focused inventory/comparison fixtures, deterministic JSON/text coverage, reportschema_versionbump, Python issue rendering;spec_coverage_test.rsstays a thin consumer.This closes the chain: spec ↔ enum variants (guarded since #290) and enum variants ↔
VALUES(new check), soVALUESis transitively spec-accurate.3. CLI: delete the
KNOWN_*constantsparse_serde_enum::<ServicePostRequestRegion>(value, "region", ServicePostRequestRegion::VALUES)— error messages ("expected one of …") now sourced from the library.PossibleValuesParser::new(PgVersion::VALUES), so--helpfor the Postgres flags updates automatically.Populating the new
VALUESconsts from the current enums fixes theeurope-west2/ca-central-1gaps as a side effect.Result
A spec drift remediation that adds an enum value flows automatically: spec → analyzer finding →
models.rsvariant +VALUES(analyzer fails CI ifVALUESis not updated) → CLI validation, error messages, and--helpupdate with zero CLI edits.Out of scope / already fine
pg create --sizedeliberately takes a free-formString("Server validates — accepts any value"), soPgSizeneeds no CLI change orVALUESconst.PgStateProperty(stopped, added in OpenAPI drift: 36 gaps between live spec and library #298) have no CLI validation surface.VALUESconsts on the hundreds of enums without a CLI surface.🤖 Generated with Claude Code