Files
promptkit/docs/roadmap/implementation.md

17 KiB

Optional Request-Parameter Omission Implementation Plan

Status: Complete.

Purpose

This document is the decision-complete implementation plan for omitting unset optional request parameters. 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 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:

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:

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:

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.