diff --git a/docs/roadmap/implementation.md b/docs/roadmap/implementation.md index 44ec7aa..007d66d 100644 --- a/docs/roadmap/implementation.md +++ b/docs/roadmap/implementation.md @@ -626,6 +626,11 @@ documentation is updated. focused changed-seam cases retain final values, paths and relevant error meaning. This stage changes organization, not configuration acceptance policy. +Completion: final-handoff traversal retains its resource index and delegates +backend, profile, and prompt records to focused validators. Configuration tests +and focused public configuration, validation, inspection, and source tests pass +on Go 1.27.3; the extracted checks preserve their existing paths and rules. + ## Stage 24: Separate Prompt Capture And Effective Target Construction **Catalog:** 22, completion. **Files:** configuration prompts/targets and tests. diff --git a/internal/configuration/load.go b/internal/configuration/load.go index 149fb05..f3996c6 100644 --- a/internal/configuration/load.go +++ b/internal/configuration/load.go @@ -419,101 +419,110 @@ func validateEffective(config *document, validation *configkit.Validation) { } for _, resources := range sets { for index, backend := range resources.backends { - base := resources.bases.backends.Index(index) - validateBackendEndpoints(backend, base, validation) - if !domain.IsValidLocalResourceKey(backend.ID) { - validation.Error(base.Key("id"), "backend ID must be a nonblank local key without a slash") - } - if backend.Endpoint == nil || *backend.Endpoint == "" { - validation.Error(base.Key("endpoint"), "endpoint is required") - } - if backend.APIKeyEnv != nil && *backend.APIKeyEnv != "" && !envName.MatchString(*backend.APIKeyEnv) { - validation.Error(base.Key("api_key_env"), "environment variable name is invalid") - } - if backend.ConcurrencyLimit != nil && *backend.ConcurrencyLimit < 0 { - validation.Error(base.Key("concurrency_limit"), "concurrency limit must not be negative") - } - if backend.QueueCapacity != nil { - if *backend.QueueCapacity < 0 { - validation.Error(base.Key("queue_capacity"), "queue capacity must not be negative") - } - if backend.ConcurrencyLimit == nil || *backend.ConcurrencyLimit <= 0 { - validation.Error(base.Key("queue_capacity"), "queue capacity requires a positive concurrency limit") - } else if *backend.QueueCapacity > int(^uint(0)>>1)-*backend.ConcurrencyLimit { - validation.Error(base.Key("queue_capacity"), "total capacity overflows int") - } - } - validateExtraParams(backend.ExtraParams, base.Key("extra_params"), true, validation) - if smokeTests := backend.SmokeTests; smokeTests != nil { - smokePath := base.Key("smoke_tests") - if smokeTests.DefaultProfile != nil { - if strings.TrimSpace(*smokeTests.DefaultProfile) == "" { - validation.Error(smokePath.Key("default_profile"), "default profile must not be blank") - } else if !domain.IsValidResourceID(*smokeTests.DefaultProfile) { - validation.Error(smokePath.Key("default_profile"), "default profile reference is malformed") - } - } - if authentication := smokeTests.Authentication; authentication != nil { - kindPath := smokePath.Key("authentication").Key("kind") - if authentication.Kind == nil { - validation.Error(kindPath, "authentication kind is required") - } else if *authentication.Kind != "none" && *authentication.Kind != "openrouter_key" { - validation.Error(kindPath, "authentication kind must be none or openrouter_key") - } - } - } + validateBackendRecord(backend, resources.bases.backends.Index(index), validation) } for index, profile := range resources.profiles { - base := resources.bases.profiles.Index(index) - if profile.Endpoint != nil && *profile.Endpoint != "" { - if _, err := domain.NormalizeOpenAICompatibleBaseEndpoint(*profile.Endpoint); err != nil { - validation.Error(base.Key("endpoint"), err.Error()) - } - } - if !domain.IsValidLocalResourceKey(profile.ID) { - validation.Error(base.Key("id"), "profile ID must be a nonblank local key without a slash") - } - if profile.Backend != nil && *profile.Backend != "" { - if !domain.IsValidResourceID(*profile.Backend) { - validation.Error(base.Key("backend"), "backend reference is malformed") - } else if _, exists := backendIDs[*profile.Backend]; !exists { - validation.Error(base.Key("backend"), "backend is not defined") - } - } - if profile.Model != nil && *profile.Model != "" && strings.TrimSpace(*profile.Model) == "" { - validation.Error(base.Key("model"), "model must not be blank") - } - if profile.Temperature != nil { - if err := domain.ValidateExecutionTargetSettings(domain.ExecutionTarget{Temperature: *profile.Temperature}); err != nil { - validation.Error(base.Key("temperature"), err.Error()) - } - } - if profile.TopP != nil { - if err := domain.ValidateExecutionTargetSettings(domain.ExecutionTarget{TopP: *profile.TopP}); err != nil { - validation.Error(base.Key("top_p"), err.Error()) - } - } - if profile.MaxTokens != nil { - if err := domain.ValidateExecutionTargetSettings(domain.ExecutionTarget{MaxTokens: *profile.MaxTokens}); err != nil { - validation.Error(base.Key("max_tokens"), err.Error()) - } - } - if profile.TimeoutSeconds != nil { - if err := domain.ValidateExecutionTargetSettings(domain.ExecutionTarget{TimeoutSeconds: *profile.TimeoutSeconds}); err != nil { - validation.Error(base.Key("timeout_seconds"), err.Error()) - } - } - validateExtraParams(profile.ExtraParams, base.Key("extra_params"), false, validation) + validateProfileRecord(profile, resources.bases.profiles.Index(index), backendIDs, validation) } for index, prompt := range resources.prompts { - base := resources.bases.prompts.Index(index) - if !domain.IsValidLocalResourceKey(prompt.ID) { - validation.Error(base.Key("id"), "prompt ID must be a nonblank local key without a slash") - } - if prompt.DefaultProfile != nil && *prompt.DefaultProfile != "" && !domain.IsValidResourceID(*prompt.DefaultProfile) { - validation.Error(base.Key("default_profile"), "default profile reference is malformed") + validatePromptRecord(prompt, resources.bases.prompts.Index(index), validation) + } + } +} + +func validateBackendRecord(backend backendConfig, base configkit.Path, validation *configkit.Validation) { + validateBackendEndpoints(backend, base, validation) + if !domain.IsValidLocalResourceKey(backend.ID) { + validation.Error(base.Key("id"), "backend ID must be a nonblank local key without a slash") + } + if backend.Endpoint == nil || *backend.Endpoint == "" { + validation.Error(base.Key("endpoint"), "endpoint is required") + } + if backend.APIKeyEnv != nil && *backend.APIKeyEnv != "" && !envName.MatchString(*backend.APIKeyEnv) { + validation.Error(base.Key("api_key_env"), "environment variable name is invalid") + } + if backend.ConcurrencyLimit != nil && *backend.ConcurrencyLimit < 0 { + validation.Error(base.Key("concurrency_limit"), "concurrency limit must not be negative") + } + if backend.QueueCapacity != nil { + if *backend.QueueCapacity < 0 { + validation.Error(base.Key("queue_capacity"), "queue capacity must not be negative") + } + if backend.ConcurrencyLimit == nil || *backend.ConcurrencyLimit <= 0 { + validation.Error(base.Key("queue_capacity"), "queue capacity requires a positive concurrency limit") + } else if *backend.QueueCapacity > int(^uint(0)>>1)-*backend.ConcurrencyLimit { + validation.Error(base.Key("queue_capacity"), "total capacity overflows int") + } + } + validateExtraParams(backend.ExtraParams, base.Key("extra_params"), true, validation) + if smokeTests := backend.SmokeTests; smokeTests != nil { + smokePath := base.Key("smoke_tests") + if smokeTests.DefaultProfile != nil { + if strings.TrimSpace(*smokeTests.DefaultProfile) == "" { + validation.Error(smokePath.Key("default_profile"), "default profile must not be blank") + } else if !domain.IsValidResourceID(*smokeTests.DefaultProfile) { + validation.Error(smokePath.Key("default_profile"), "default profile reference is malformed") } } + if authentication := smokeTests.Authentication; authentication != nil { + kindPath := smokePath.Key("authentication").Key("kind") + if authentication.Kind == nil { + validation.Error(kindPath, "authentication kind is required") + } else if *authentication.Kind != "none" && *authentication.Kind != "openrouter_key" { + validation.Error(kindPath, "authentication kind must be none or openrouter_key") + } + } + } +} + +func validateProfileRecord(profile profileConfig, base configkit.Path, backendIDs map[string]struct{}, validation *configkit.Validation) { + if profile.Endpoint != nil && *profile.Endpoint != "" { + if _, err := domain.NormalizeOpenAICompatibleBaseEndpoint(*profile.Endpoint); err != nil { + validation.Error(base.Key("endpoint"), err.Error()) + } + } + if !domain.IsValidLocalResourceKey(profile.ID) { + validation.Error(base.Key("id"), "profile ID must be a nonblank local key without a slash") + } + if profile.Backend != nil && *profile.Backend != "" { + if !domain.IsValidResourceID(*profile.Backend) { + validation.Error(base.Key("backend"), "backend reference is malformed") + } else if _, exists := backendIDs[*profile.Backend]; !exists { + validation.Error(base.Key("backend"), "backend is not defined") + } + } + if profile.Model != nil && *profile.Model != "" && strings.TrimSpace(*profile.Model) == "" { + validation.Error(base.Key("model"), "model must not be blank") + } + if profile.Temperature != nil { + if err := domain.ValidateExecutionTargetSettings(domain.ExecutionTarget{Temperature: *profile.Temperature}); err != nil { + validation.Error(base.Key("temperature"), err.Error()) + } + } + if profile.TopP != nil { + if err := domain.ValidateExecutionTargetSettings(domain.ExecutionTarget{TopP: *profile.TopP}); err != nil { + validation.Error(base.Key("top_p"), err.Error()) + } + } + if profile.MaxTokens != nil { + if err := domain.ValidateExecutionTargetSettings(domain.ExecutionTarget{MaxTokens: *profile.MaxTokens}); err != nil { + validation.Error(base.Key("max_tokens"), err.Error()) + } + } + if profile.TimeoutSeconds != nil { + if err := domain.ValidateExecutionTargetSettings(domain.ExecutionTarget{TimeoutSeconds: *profile.TimeoutSeconds}); err != nil { + validation.Error(base.Key("timeout_seconds"), err.Error()) + } + } + validateExtraParams(profile.ExtraParams, base.Key("extra_params"), false, validation) +} + +func validatePromptRecord(prompt promptConfig, base configkit.Path, validation *configkit.Validation) { + if !domain.IsValidLocalResourceKey(prompt.ID) { + validation.Error(base.Key("id"), "prompt ID must be a nonblank local key without a slash") + } + if prompt.DefaultProfile != nil && *prompt.DefaultProfile != "" && !domain.IsValidResourceID(*prompt.DefaultProfile) { + validation.Error(base.Key("default_profile"), "default profile reference is malformed") } }