The implementation target is message-level Anthropic/OpenRouter-style cache control for rendered prompt messages, plus cache usage observability. Do not implement deferred roadmap items such as `content_blocks`, top-level `cache_control`, or general `extra_params` serialization.
- Keep provider request serialization in `internal/llm`.
- Keep prompt loading in `internal/promptdef`.
- Update canonical non-roadmap docs only when the behavior is implemented.
- Do not accept or emit raw secret values.
## Stage 1: Domain Types And Prompt Loader
Objective: make prompt YAML accept and validate optional `messages[].cache_control`, and carry normalized cache-control metadata in domain prompt definitions.
Files to update:
-`internal/domain/domain.go`
-`internal/promptdef/filesystem_repository.go`
-`internal/promptdef/repository_test.go`
-`internal/promptdef/testdata/`
Domain decisions:
- Add a typed cache-control value in `internal/domain`.
- Use a pointer on message structs so absence is distinguishable from an empty object.
- Use `omitempty` on JSON tags for the new field so existing no-cache prompt and render hashes do not gain `null` fields.
- Add a private `cacheControlFile` struct with `type` and `ttl`.
- In `normalizePromptDefinition`, validate `cache_control` per message.
- Trim whitespace from `type` and `ttl`.
- Reject present `cache_control` when `type` is empty.
- Reject any `type` other than `ephemeral`.
- Accept omitted or empty `ttl`.
- Reject any non-empty `ttl` other than `1h`.
- Copy normalized values into `domain.PromptMessageTemplate.CacheControl`.
- Preserve the existing rule that each message must set exactly one of `content` or `content_file`.
Testing requirements:
- Add a valid fixture with `cache_control: {type: ephemeral, ttl: 1h}`.
- Add a valid fixture with `cache_control: {type: ephemeral}`.
- Add invalid fixtures for:
- empty `cache_control.type`
- unsupported `cache_control.type`
- unsupported `cache_control.ttl`
- unknown field under `cache_control`
- Assert valid fixtures load with the expected `CacheControl` values.
- Assert invalid fixtures fail with `promptdef.ErrInvalidPromptDefinition` or `promptdef.ErrInvalidYAML` as appropriate.
- Re-run:
```bash
go test ./internal/promptdef
```
Do not update renderer, formatter, LLM serialization, adapters, or user-facing docs in this stage.
## Stage 2: Rendering, Prepared Output, And Render Hashing
Objective: preserve cache-control metadata through rendering, expose it in prepared-run output, and make `rendered_prompt_hash` reflect outbound-affecting cache metadata.
- Split request and response wire message structs. The current `openAIChatMessage` is used in both request and response paths; changing request content to `any` should not make response decoding less strict than necessary.
If adding a new example prompt with `cache_control`, also smoke-render that prompt with `--format json` and inspect that the rendered message includes `cache_control`.
Final review checklist:
- Existing prompt definitions without `cache_control` still load.
- Prompt definitions with `cache_control` render successfully.
- Unknown cache-control YAML fields are rejected.
- No-cache outbound messages still serialize with string `content`.
- Cache-controlled outbound messages serialize with text content-block arrays.
-`rendered_prompt_hash` changes when cache-control metadata changes.
- Cache usage fields are zero when providers omit them.
- CLI and HTTP request surfaces have no new knobs.
- CLI and HTTP response surfaces expose cache usage.
- Non-roadmap docs describe only implemented behavior.
-`docs/roadmap/cache.md` still defines target state and deferred work, not task steps.
## Stage Boundaries
Stages 1 through 4 can be implemented in separate prompts if needed. Stage 5 should happen only after behavior is implemented. Stage 6 should be performed after all implementation and documentation changes are complete.
If a stage fails because prior stage changes were not implemented, stop and complete the earlier stage first rather than papering over missing domain fields or DTO mappings.
## Open Questions
None required for the initial implementation.
The plan chooses message-level `cache_control`, string content for legacy messages, text-block array content only when cache control is present, and no new CLI/HTTP request knobs. These choices match `docs/roadmap/cache.md` and preserve existing prompt-definition compatibility.