# Optional Request-Parameter Omission Implementation Plan **Status:** Ready for implementation. ## Purpose This document is the decision-complete implementation plan for [omitting unset optional request parameters](optional-request-parameters.md). It is written for a `gpt-5.6-terra` coding agent that will implement each stage in order. The feature roadmap owns the motivation, policy choices, compatibility boundary, non-goals, and target end state. This document owns the concrete design, file-level work, test ownership, documentation updates, validation, and completion gates. The separate [application fallback profiles](fallback-profiles.md) roadmap is not part of this implementation plan. Preserve it unchanged for its own later planning and implementation cycle. ## Implementation Rules - Complete the stages in order. Stage 1 must leave code, tests, GoDoc, and current-state documentation mutually accurate; Stage 2 performs the final audit and repository-wide acceptance. - Preserve unrelated working-tree changes. In particular, do not edit, implement, retire, or reclassify `fallback-profiles.md`. - Follow every policy under `docs/policy/`, the task-specific reading guide in `docs/development.md`, and the accepted behavior in `optional-request-parameters.md`. - Keep the existing package boundaries. Framework defaults remain in `internal/defaults`, resolution remains in `internal/usecase`, and outbound OpenAI-compatible serialization remains in `internal/llm`. - Do not add an exported type, field, option, method, error, or public package. This feature changes default and wire semantics within existing contracts. - Do not replace numeric profile fields with pointers or add profile presence tracking. File and in-memory profile zero values retain their existing inheritance semantics; runtime pointer overrides remain the only supported way to select an explicit numeric zero. - Preserve required request fields, session IDs, structured output, credentials, extra-parameter validation, reasoning clearing, deadlines, capacity management, and all existing precedence rules. - Do not query a provider for defaults or capabilities and do not add backend- or model-specific serialization branches. - Keep tests lean and behavioral. Use the existing root precedence test to own resolved public/injected-client metadata and the existing model-client tests to own wire inclusion and omission. Do not duplicate those matrices in a new end-to-end fixture. - Update exact exported semantics in GoDoc, profile/default semantics in `docs/formats.md`, and provider request-body semantics in `docs/integrations/openai-compatible-chat.md` in the same stage as the code. - Do not add release notes, change a module version, commit, tag, push, or publish a release as part of this plan. ## Fixed Design ### Framework Defaults In `internal/defaults/defaults.go`, remove these constants: ```go ExecutionDefaultTemperature ExecutionDefaultMaxTokens ExecutionDefaultTopP ``` They currently encode zero for `temperature` and `max_tokens` and one for `top_p`. Optional provider controls are no longer framework defaults, so retaining zero-valued constants under default-oriented names would obscure the new contract. Keep `ExecutionDefaultTimeoutSeconds` at its current positive value. Timeout is a Promptkit-owned generation deadline and is not an OpenAI-compatible request body field. Keep `ExecutionTargetDefault` as the common resolution baseline, but have it initialize only `TimeoutSeconds`. The zero Go values for `Temperature`, `MaxTokens`, and `TopP` then represent unspecified provider controls. Do not rename this internal function or add a second defaults constructor. ### Resolution And Public Metadata Do not change the merge functions or precedence in `internal/usecase/runner.go`: 1. the baseline target supplies only the Promptkit timeout; 2. nonzero profile numeric fields replace the baseline; 3. non-nil runtime numeric overrides replace profile values; and 4. `ExecutionTargetPresence` records runtime overrides, including explicit zero values. Consequently, when a profile omits the optional provider controls, resolved `ExecutionTarget` values contain zero for `Temperature`, `MaxTokens`, and `TopP`. That zero is stable metadata for “unspecified” unless the accompanying `GenerateRequest.TargetPresence` bit reports an explicit runtime zero. Do not expose target presence in `PreparedRun`, `RunResult`, or `ProfileInspection`, and do not change their stable JSON shapes. As already true for `max_tokens`, those metadata values report the resolved numeric value rather than provenance. A prepared result containing `top_p: 0` therefore does not distinguish an unspecified value from an explicit runtime zero; injected clients receive the separate presence value when the distinction affects execution. Update root GoDoc in `types.go` so it no longer calls an unspecified optional provider control an effective provider value: - `ExecutionTarget.Temperature`, `MaxTokens`, and `TopP` must each state that zero leaves the field unspecified to compatible providers unless the corresponding `ExecutionTargetPresence` bit is true; - `ExecutionTarget.TimeoutSeconds` retains its existing deadline semantics; - `Profile` and `ExecutionTargetOverride` documentation must describe zero or nil as inheriting a lower-precedence value and otherwise leaving the provider control unspecified, rather than implying that every field receives a concrete framework value; and - `PreparedRun`, `ProfileInspection`, and other effective-target summaries may continue to describe precedence, but must not imply that Promptkit knows a provider's omitted default. Do not change field types, field order, JSON tags, conversion functions, string formatting, or copying behavior. ### Outbound Request Semantics The current built-in client already has the required mechanism: `openAIChatRequestFromGenerateRequest` includes `temperature`, `max_tokens`, or `top_p` when the resolved value is nonzero or the corresponding target-presence bit is true, and `openAIChatRequestPayload` omits nil fields. Preserve that logic. No production change should be needed in `internal/llm/openai_compatible_client.go`. Change it only if a focused failing test demonstrates that the existing implementation does not meet this plan; do not special-case `top_p`, inspect profile provenance, or move framework default policy into the transport. The resulting behavior is: - an omitted profile `top_p` resolves to zero and is absent from the body; - a nonzero profile or runtime `top_p` is included; - an explicit runtime `top_p` of zero is included because presence is true; - the same rules continue to apply to `temperature` and `max_tokens`; - empty `service_tier` and effective `reasoning_effort` remain absent; - configured `extra_params` remain present after validation; and - `model`, `messages`, conditional `session_id`, and conditional `response_format` remain unchanged. ### Profile Formats And Built-In Profiles Do not change YAML or public `Profile` field shapes. Numeric zero in a file or in-memory profile continues to mean “do not replace the lower layer.” With no lower provider value, zero therefore resolves to unspecified. An explicit profile-level numeric zero remains unsupported; consumers use a runtime pointer override when zero itself must be sent. Do not edit files under `internal/profile/builtin/assets/`. Values declared in those files are explicit profile policy and remain effective. Existing nonzero-profile tests are sufficient to protect explicit inclusion; do not add one test per built-in asset or parameter. ### Test Ownership Use these existing boundaries: - In `engine_test.go`, update the “framework defaults” row of `TestEngineExecutionSettingPrecedence` so the zero-valued profile expects `TopP: 0` while retaining `Temperature: 0`, `MaxTokens: 0`, and the positive timeout. Rename that row to describe unspecified provider controls plus the framework timeout. Keep the rows proving nonzero profile precedence and explicit runtime-zero presence unchanged. - Remove `TestRunnerRunBuiltInDefaultsUsedWhenProfileOmitsOptionalFields` from `internal/usecase/runner_test.go`. Its literal-default assertions duplicate the stronger assembled root precedence test and depend on the internal constants being removed. Do not replace it with another internal default-value test. - Keep `TestOpenAICompatibleClientOmitsImplicitZeroNumericFields` and `TestOpenAICompatibleClientSerializesExplicitZeroNumericOverrides` in `internal/llm/openai_compatible_client_test.go`. Together they own the wire distinction and should pass without weakening their assertions. - Keep the existing nonzero request serialization and profile-precedence tests passing. They prove that explicitly configured values continue to be sent and selected. Do not add snapshots, golden files, provider calls, or a broad duplicate integration test. Add a new test only if the implementation exposes a distinct contract risk not covered by the tests above, and record that reason in the test name or nearby test structure rather than in a new planning document. ### Canonical Documentation Update current-state documentation in Stage 1: - In `docs/formats.md`, replace the optional provider-control entries in the framework-default table with clear unspecified/omitted semantics, while retaining the positive `timeout_seconds` framework default. Explain that profile numeric zero inherits a lower layer and otherwise remains unspecified; an explicit runtime pointer zero is retained. - In `docs/integrations/openai-compatible-chat.md`, state that `temperature`, `max_tokens`, and `top_p` are included only when selected by a profile or runtime override, including explicit runtime zero, and are absent when unspecified. Keep the existing ownership of required fields, `session_id`, structured output, extra parameters, and timeout behavior. - In `types.go`, apply the GoDoc changes described above; these declarations own the exact public value semantics. Do not add a README or release-document note. The consumer guide already routes exact field behavior to GoDoc and profile/default behavior to the format reference, so do not duplicate the new contract there. The internal LLM document describes flow rather than exact field omission and does not require a change unless its current text is found to contradict the implementation. ## Stage 1: Implement Omission Semantics And Canonical Contracts ### Objective Remove optional provider controls from the framework baseline, preserve explicit profile and runtime values, update the canonical contracts, and prove the behavior at the existing resolution and wire boundaries. ### Implementation Prompt Implement only Stage 1 of `docs/roadmap/implementation.md`. Read the complete feature roadmap, implementation rules, and fixed design above before editing. 1. In `internal/defaults/defaults.go`, remove the three provider-control constants and make `ExecutionTargetDefault` initialize only `TimeoutSeconds`. 2. In `engine_test.go`, update and rename the default-precedence table row exactly as described under Test Ownership. 3. Remove the redundant literal-default test from `internal/usecase/runner_test.go`; do not weaken other precedence, profile-value, or runtime-zero tests. 4. Update the affected exported GoDoc in `types.go` without changing any declaration, JSON tag, or serialization shape. 5. Update `docs/formats.md` and `docs/integrations/openai-compatible-chat.md` according to Canonical Documentation. 6. Run the focused validation below. Repair regressions in scope, but do not broaden the feature or change the established serializer merely to make a mistaken expectation pass. Do not edit built-in profile assets, fallback-profile work, backend registration, profile parsing, target merge logic, public value shapes, prepared-execution lifecycle, capacity management, or release material. ### Focused Validation Run from the repository root: ```sh gofmt -w internal/defaults/defaults.go types.go engine_test.go \ internal/usecase/runner_test.go go test . -run 'TestEngineExecutionSettingPrecedence' go test ./internal/llm -run \ 'TestOpenAICompatibleClient(GenerateSuccess|OmitsImplicitZeroNumericFields|SerializesExplicitZeroNumericOverrides)' go test ./internal/usecase -run \ 'Test(ResolveExecutionTarget|RunnerPrepareRequestNumericOverridePresence|RunnerPrepareSelectedProfileBeatsBuiltInDefault|RunnerRunSelectedProfileBeatsBuiltInDefault)' go test . ./internal/defaults ./internal/usecase ./internal/llm go vet . ./internal/defaults ./internal/usecase ./internal/llm git diff --check ``` If a focused regular expression does not match an existing test name, inspect the current names and run the narrowest equivalent set; do not silently skip the intended resolution, explicit-profile, explicit-zero, and wire-omission coverage. ### Completion Gate Stage 1 is complete only when: - the resolution baseline contains no provider tuning value and retains the Promptkit timeout; - an omitted `top_p` resolves to zero and the built-in client omits it; - nonzero profile and runtime values remain effective and serialized; - explicit runtime zero values remain distinguishable and serialized through `ExecutionTargetPresence`; - no public type or stable JSON shape changed; - required fields, structured output, session IDs, extra parameters, reasoning, credentials, and deadlines retain their existing behavior; - GoDoc, format documentation, and the integration contract describe the implemented behavior without conflicting ownership; and - all focused tests, vet, formatting, and whitespace checks pass. ## Stage 2: Audit Compatibility And Validate The Repository ### Objective Confirm that the narrow semantic change is complete across all public, injected-client, built-in-profile, documentation, and repository surfaces, then mark the temporary planning documents complete. ### Implementation Prompt Implement only Stage 2 of `docs/roadmap/implementation.md` after Stage 1 satisfies its completion gate. 1. Search tracked Go and Markdown files for the removed constant names, framework `top_p` defaults, claims that all effective provider controls have concrete framework values, and request-body inclusion rules. Correct only stale statements or tests owned by this feature. 2. Confirm that `internal/profile/builtin/assets/` has no feature-related diff and that its explicit nonzero optional controls still pass ordinary profile validation and resolution tests. 3. Confirm that `internal/llm/openai_compatible_client.go` either has no diff or contains only a change required by a focused failing contract test. The default policy must remain outside the transport. 4. Follow every changed Markdown link and confirm its target exists. Verify that current-state documents describe implemented behavior and that exact contracts remain with GoDoc, the format reference, and the integration contract. 5. Run the complete validation sequence below and repair only in-scope failures. 6. After all checks pass, change the status of `optional-request-parameters.md` and this document to `Complete`. Do not change the status or contents of `fallback-profiles.md`. 7. Re-run `git diff --check` and inspect the final working tree and diff. Do not delete temporary roadmaps in this stage; retirement is a separate maintainer action. Do not add release notes, change versions, or create a commit, tag, push, or release. ### Full Validation Run from the repository root: ```sh gofmt -w internal/defaults/defaults.go types.go engine_test.go \ internal/usecase/runner_test.go gofmt -l $(git ls-files '*.go') go test ./... go test -race ./... go vet ./... go build ./... go run ./examples/go-library/prepare git diff --check git status --short ``` The `gofmt -l` command must print no paths. The maintained example must remain offline and require no real credential or provider. Inspect the final diff and confirm: - only this feature's files and pre-existing user changes are present; - no built-in profile asset, public declaration shape, stable JSON tag, credential rule, workspace file, local module replacement, generated binary, or unrelated formatting changed; - the removed provider-default constants have no remaining references; - the provider omission policy is implemented by resolution plus the existing generic serializer, not by a `top_p` transport special case; - the optional-parameter roadmap and this plan are complete while the fallback roadmap remains selected; and - no commit, tag, push, or release was created. ### Completion Gate The implementation is complete only when: - every Stage 1 gate remains satisfied; - the ordinary and race-enabled suites pass; - vet, build, formatting, the maintained offline example, Markdown links, and whitespace checks pass; - public metadata, injected-client presence, profile inheritance, and outbound omission semantics are mutually consistent; - explicit built-in and consumer profile values retain their behavior; - both feature-specific roadmap statuses are `Complete`; - `fallback-profiles.md` remains unchanged and selected for later work; and - the repository is ready for maintainer review without a commit or release having been created by this plan. ## Open Questions None. The feature roadmap and fixed design above fully specify the behavior, compatibility boundary, implementation, documentation ownership, and test strategy.