feat: add JSON Schema for config.yaml with typed config accessors#8630
Draft
wpfleger96 wants to merge 1 commit intomainfrom
Draft
feat: add JSON Schema for config.yaml with typed config accessors#8630wpfleger96 wants to merge 1 commit intomainfrom
wpfleger96 wants to merge 1 commit intomainfrom
Conversation
e7aff91 to
ba853db
Compare
Goose has no machine-readable schema for config.yaml and config keys
are accessed via untyped string literals scattered across the codebase.
This adds a JSON Schema for IDE autocomplete/validation and makes
GooseConfigSchema the authoritative registry of user-facing config
keys, with compile-time enforcement linking it to typed accessors.
Three enforcement layers prevent config key drift:
- const assert in config_value! validates keys exist in the schema
- Typed accessors replace raw get_param("KEY") string literals
- Test-based lint catches new untyped access outside allowlisted files
ba853db to
5a4bb00
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
crates/goose/config.schema.json) forconfig.yaml, enabling IDE autocomplete and programmatic validationGooseConfigSchemathe authoritative registry of user-facing config keys with compile-time enforcementget_param("STRING_KEY")call sites across 35 files to typed accessors (config.get_openai_host(),config.get_goose_max_turns(), etc.)Motivation
Goose is the only major AI coding CLI without a machine-readable config schema. Codex, Gemini CLI, and Claude Code all publish JSON Schemas for IDE autocomplete and validation. Beyond the schema gap, config keys were accessed via untyped string literals scattered across the codebase -- adding a new key required no schema update, and drift between the schema and runtime access was undetectable.
How it works
Three enforcement layers prevent config key drift:
Compile-time assertion -- the
config_value!macro now includesconst assert!(GooseConfigSchema::has_key(stringify!($key))). Adding aconfig_value!for a key not registered in the schema struct is a compile error.Typed accessors replace string literals -- ~90
config_value!invocations generateget_*/set_*methods onConfig. Callers useconfig.get_openai_host()instead ofconfig.get_param("OPENAI_HOST"). Same return type (Result<T, ConfigError>), same IO path, just type-safe at the call site.Test-based lint -- a
#[test]scans all.rssource files forget_param("...")/set_param("...")calls with literal string arguments, asserting they only appear in allowlisted files. New untyped access fails the test suite.Key taxonomy (what gets typed vs what stays dynamic):
config_value!typed accessorGOOSE_MODEL,OPENAI_HOST,GOOSE_CLI_THEMEget_paramextensions,gateway_configs,experimentsget_param, allowlisted in lintTELEMETRY_ENABLED_KEY,GOOSE_RECIPE_GITHUB_REPO_CONFIG_KEYWhat does NOT change:
get_param/set_paramremain public (needed by Category B/C callers)File changes
crates/goose/src/config/schema.rsNew file. Defines
GooseConfigSchemastruct with ~120 fields for JSON Schema generation. AddsALL_KEYSconst array +has_key()const fn for compile-time validation. Two tests: struct↔array consistency check, and source-scanning lint for untyped config access.crates/goose/src/config/base.rsAdds
const assert!to both forms of theconfig_value!macro. Adds ~70 newconfig_value!invocations covering all Category A config keys (providers, core settings, CLI, security, observability, tunnel).crates/goose/src/bin/generate_config_schema.rsNew binary. Generates
config.schema.jsonfromGooseConfigSchemaviaschemars::schema_for!.crates/goose/config.schema.jsonGenerated schema output (1430 lines). 121 properties with correct types, discriminated union for extension configs.
crates/goose/src/config/goose_mode.rs,crates/goose/src/slash_commands.rs,crates/goose/src/agents/extension.rs,crates/goose/src/config/extensions.rsAdded
#[derive(JsonSchema)]to existing types so the schema references real types rather than duplicating definitions.crates/goose/src/providers/*.rs(18 files)Mechanical migration:
config.get_param("KEY")→config.get_key()for all provider config keys.crates/goose/src/agents/*.rs,crates/goose/src/context_mgmt/mod.rs,crates/goose/src/security/*.rs,crates/goose/src/posthog.rs,crates/goose/src/hints/load_hints.rs,crates/goose/src/model.rs,crates/goose/src/otel/otlp.rsSame mechanical migration for core Goose settings, security, observability, and telemetry keys.
crates/goose-cli/src/session/*.rs,crates/goose-cli/src/commands/configure.rs,crates/goose-cli/src/recipes/*.rsMigrated CLI config access. Deleted Category D constants (
TELEMETRY_ENABLED_KEY,GOOSE_RECIPE_GITHUB_REPO_CONFIG_KEY).crates/goose-server/src/tunnel/mod.rsMigrated
tunnel_auto_startto typed accessor.JustfileAdded
generate-config-schemaandcheck-config-schemarecipes. Added config schema check tocheck-everything..github/workflows/ci.ymlAdded "Check Config Schema is Up-to-Date" step to the
schema-checkCI job.Reproduction steps
just check-config-schema-- should pass (schema is up-to-date)cargo test -p goose --lib all_keys_matches-- validatesALL_KEYSmatches struct fieldscargo test -p goose --lib no_untyped_config_access-- validates no literal-stringget_paramcalls outside allowlistconfig_value!(FAKE_KEY, String)tobase.rswithout adding it toGooseConfigSchema-- should fail to compileconfig.get_param("GOOSE_MODEL")to any non-allowlisted file --no_untyped_config_accesstest should catch itconfig.yamlin VS Code with YAML extension and point$schemaat the raw GitHub URL ofconfig.schema.json-- should get autocomplete for all config keys