Skip to content

feat!: reject unrecognized model_config settings and bump coder/coder to latest main#388

Merged
ethanndickson merged 11 commits into
mainfrom
agents-model-config-drop-validator
Jul 13, 2026
Merged

feat!: reject unrecognized model_config settings and bump coder/coder to latest main#388
ethanndickson merged 11 commits into
mainfrom
agents-model-config-drop-validator

Conversation

@ethanndickson

@ethanndickson ethanndickson commented Jul 13, 2026

Copy link
Copy Markdown
Member

Closes CODAGT-797

Adds a plan-time validator for coderd_agents_model.model_config that fails loudly when the config contains settings the provider would silently drop when decoding into its pinned Coder SDK.

Why

Each provider release pins exactly one codersdk, and model_config is decoded into that SDK's ChatModelCallConfig before every create/update — so a release supports exactly one model_config schema. Standard encoding/json ignores object keys with no matching struct field, so anything outside that schema (a misspelled key, a field from a newer Coder, a field removed or renamed by an SDK bump) is silently dropped in the provider before the request is ever sent, with no plan diff, and the value in state compares semantically equal. A user can lose configured behavior with no signal.

What

  • New agentsModelConfigNoDroppedKeysValidator, wired into the model_config schema alongside the existing not-empty validator.
  • It reports every dropped setting by its full dotted path (e.g. provider_options.anthropic.bogus_setting), so a name that is valid in one place cannot mask a dropped occurrence of the same name elsewhere.
  • The error explains that the settings are not part of the schema this provider was built against, and tells the user to either upgrade the provider to a release built against their Coder version or fix/remove the field.

This is schema-change-agnostic: nothing in the implementation restates the SDK schema, so it cannot drift when ChatModelCallConfig evolves.

Design notes
  • The SDK is the sole oracle. To decide whether a key is dropped, the validator removes it from the decoded document, re-canonicalizes through ChatModelCallConfig, and compares against the canonical output of the full document: a key whose removal leaves the output unchanged contributed nothing, so it was dropped. Nothing hardcodes recognized fields, legacy aliases, or folding rules.
  • Relocations and legacy aliases fall out for free. A legacy top-level pricing key on its own (folded under cost) probes as kept, because removing it changes the output. The same key shadowed by a differing cost.<key> (whose nested value wins) probes as dropped, because removing the ignored legacy value changes nothing — so silent overrides are still caught.
  • Content-free values are skipped. null, [], and {} hit omitempty whether or not the key is recognized, so removing one never changes the output; the probe cannot judge them and does not try.
  • json.DisallowUnknownFields does not work here: ChatModelCallConfig has a custom UnmarshalJSON that calls plain json.Unmarshal internally, discarding the decoder's strict setting for the whole subtree.
  • This branch is independent of the coder/coder bump (PR chore!: bump coder/coder go module to latest main #386): tests assert on synthetic unknown keys that drop on any SDK version.

Compatibility model

The supported way to consume this (experimental) resource is to run the provider release that matches your Coder version; cross-version compatibility is delivered by the release stream (see #386's release plan), not by any single provider binary.

We considered making model_config an opaque passthrough blob (raw JSON both ways, bypassing the typed SDK client) so one provider release could span Coder versions, and rejected it as counter to that compatibility story. Passthrough would let this one attribute float across versions while everything else about the resource stays typed to the pinned SDK — a false sense of cross-version support — and it silently accepts typos and unsupported settings, which match no Coder version, get dropped by the server on write, and never come back: a perpetual diff with no error. This validator enforces the contract loudly instead: any setting outside the schema the provider was built against fails at plan time, with the remedy in the diagnostic (fix the field name, or upgrade the provider if the setting is valid on your Coder). Combining passthrough with this validation wouldn't help either — the error rejects exactly the newer-server fields passthrough exists to forward, and softening it to a warning just re-introduces quiet drift while adding the raw-HTTP machinery passthrough requires.

The validator checks against the pinned SDK, not the live server, so the reverse mismatch — a provider newer than your Coder — can still send fields the server drops; that too is solved by matching versions.

Opened by Coder Agents on behalf of @ethanndickson.

…ently drop

Add a plan-time validator for coderd_agents_model.model_config that
canonicalizes the user's JSON through codersdk.ChatModelCallConfig and
compares object key names before and after. Any key present in the input
but gone after the round-trip is a setting Coder does not recognize and
would silently discard, so the validator raises a plan-time error naming
those keys instead of losing configuration with no diff.

json.DisallowUnknownFields cannot be used here because the SDK's custom
UnmarshalJSON calls json.Unmarshal internally, which defeats strict
decoding for the whole subtree.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c6602821b2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/provider/agents_model_config.go Outdated
Compare model_config key paths instead of bare names so a valid key can't
mask a dropped occurrence of the same name elsewhere, with an exception for
the SDK's legacy top-level pricing keys folded under cost. Also drops the
unused sliceContains helper that failed lint.
@ethanndickson

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7934eebbec

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/provider/agents_model_config.go Outdated
@ethanndickson

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 986c469266

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/provider/agents_model_config.go Outdated
@ethanndickson

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4bc9c6aca5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/provider/agents_model_config.go Outdated
@ethanndickson ethanndickson self-assigned this Jul 13, 2026
@ethanndickson

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9ff5a42293

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread internal/provider/agents_model_config.go Outdated
@ethanndickson

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Hooray!

Reviewed commit: 5ef88fb59b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@ethanndickson ethanndickson changed the title feat(internal/provider): reject model_config settings Coder would silently drop feat: reject model_config settings Coder would silently drop Jul 13, 2026
@ethanndickson ethanndickson changed the title feat: reject model_config settings Coder would silently drop feat: reject model_config settings the provider would silently drop Jul 13, 2026
@ethanndickson

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep it up!

Reviewed commit: af7bae0108

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@ethanndickson ethanndickson requested a review from johnstcn July 13, 2026 10:28

@johnstcn johnstcn left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this adds a lot of complexity for not much gain.

Do we plan eventually to promote the model config into a top-level Terraform type? Would it be simpler to just promote once it stabilizes?

}
// Invalid or non-object JSON is left for the custom type and the not-empty
// validator to report.
dropped, err := agentsModelConfigDroppedKeys(req.ConfigValue.ValueString())

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be simpler to just marshal to the SDK config type and back, and assert JSON equivalence? (leaving out the problems of ordering and whitespace)

@ethanndickson

ethanndickson commented Jul 13, 2026

Copy link
Copy Markdown
Member Author

I feel like this adds a lot of complexity for not much gain.

Yeah I think that's a reasonable criticism. The reason I'm doing this is because Mike and his agent had no idea his coder/dogfood PR to adapt the thinking levels wasn't going to do anything until the codex review bot flagged the codersdk on this repo needed bumping, but I think this applies to customers too. People pin their Terraform providers, and without this they might upgrade coder, but forgot to update the provider, and be left wondering why their config didn't apply. The provider wouldn't tell them without this.

Do we plan eventually to promote the model config into a top-level Terraform type? Would it be simpler to just promote once it stabilizes?

No plans to promote it unfortunately, when I looked at doing model_config as a top-level Terraform time it was hack after hack and hugely complex, thanks to things like fields typed with map[string]any. Also, I don't think it'll ever truly be stabilized? These AI providers seem to add and remove fields at the drop of a hat.

@johnstcn

Copy link
Copy Markdown
Member

People pin their Terraform providers, and without this they might upgrade coder, but forgot to update the provider, and be left wondering why their config didn't apply. The provider wouldn't tell them without this.

Fair point. In that case, I wonder if it would make sense to move the validation into codersdk?

@ethanndickson

Copy link
Copy Markdown
Member Author

People pin their Terraform providers, and without this they might upgrade coder, but forgot to update the provider, and be left wondering why their config didn't apply. The provider wouldn't tell them without this.

Fair point. In that case, I wonder if it would make sense to move the validation into codersdk?

Ah good shout. Yeah we could just add an UnmarshalStrict to ChatModelCallConfig that sets json.Decoder.DisallowUnknownFields, and that would let us catch this with very little code.

…ropped model_config settings

Replaces the removal-probe drop detection with
codersdk.ChatModelCallConfig.UnmarshalStrict (coder/coder#27187), pinning
the SDK to that PR's commit. The strict decoder shares the lenient path's
aux struct, so the SDK stays the sole oracle with no schema knowledge in
the provider.
Comment thread internal/provider/agents_model_resource_test.go Outdated
Comment on lines +271 to +272
"model_config contains a setting that is not part of the Coder chat model config schema this provider was built against, so it would be silently dropped before reaching Coder: %s. "+
"If the setting is valid for your Coder deployment, upgrade the provider to a version built against your Coder release; otherwise remove it or fix the field name. "+

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

non-blocking: As an end-user, how do you determine this? Something to think about in future, I suppose.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ask an agent

Yeah, not sure... Bit of an experimental-API only paper cut... We haven't had to ask users to pay attention to the provider versions like this in the past.

…xtures to top-level reasoning_effort

Folds the non-go.mod changes from #386 into this branch, since it already pins an SDK with the reasoning_effort restructure.
@ethanndickson ethanndickson changed the title feat: reject model_config settings the provider would silently drop feat!: reject unrecognized model_config settings and bump coder/coder to latest main Jul 13, 2026
@linear-code

linear-code Bot commented Jul 13, 2026

Copy link
Copy Markdown

CODAGT-797

Copy link
Copy Markdown

coder/dogfood#397 is now green and waiting on a provider release containing this SDK bump. Its current provider pin silently drops top-level reasoning_effort, which leaves one valid Codex finding unresolved. Once this PR lands and releases, I will bump dogfood, validate the rendered plan, and resume its Codex review loop.

Mux posted this dependency note on behalf of Mike.

@ethanndickson ethanndickson merged commit 6c4d21e into main Jul 13, 2026
13 checks passed
@ethanndickson ethanndickson deleted the agents-model-config-drop-validator branch July 13, 2026 14:26
@ibetitsmike

Copy link
Copy Markdown

The v0.0.22 GitHub release completed successfully, but Terraform Registry has not indexed v0.0.21 or v0.0.22. The Registry provider metadata is still coder/coderd/0.0.20 with last-modified: Wed, 01 Jul 2026, and terraform init fails with no available releases match the given constraints 0.0.22. Release assets and signatures are present, and v0.0.20 through v0.0.22 use the same signing key.

Could a maintainer check or resync the Terraform Registry publication? coder/dogfood#397 is ready to consume v0.0.22 once the Registry serves it.

Mux diagnosed and reported this release blocker on behalf of Mike.

@ethanndickson

Copy link
Copy Markdown
Member Author

The v0.0.22 GitHub release completed successfully, but Terraform Registry has not indexed v0.0.21 or v0.0.22. The Registry provider metadata is still coder/coderd/0.0.20 with last-modified: Wed, 01 Jul 2026, and terraform init fails with no available releases match the given constraints 0.0.22. Release assets and signatures are present, and v0.0.20 through v0.0.22 use the same signing key.

Could a maintainer check or resync the Terraform Registry publication? coder/dogfood#397 is ready to consume v0.0.22 once the Registry serves it.

Mux diagnosed and reported this release blocker on behalf of Mike.

Yeah I'm resyncing now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants