Align internal documentation with architecture

This commit is contained in:
2026-07-05 03:13:50 +00:00
parent 07ac7e54c5
commit 41083de46a
5 changed files with 362 additions and 254 deletions

View File

@@ -2,29 +2,32 @@
## Purpose
`internal/usecase.Runner` is the core use case orchestrator for prompt preparation and execution.
`internal/usecase.Runner` is the core prompt-execution orchestrator. It prepares prompt requests, calls the configured LLM client for `Run`, validates generated output, and returns domain results.
It owns request validation, prompt/profile resolution, runtime-parameter merge, artifact loading, prompt rendering, structured-output setup, LLM invocation, output validation, and result metadata.
Transport parsing, DTOs, CLI output, HTTP status mapping, and public package type conversion belong outside the runner.
## Inputs And Outputs
Primary input type:
Primary inputs:
- `domain.RunRequest`
- repositories/readers/renderers/validators injected at construction
- `context.Context` for cancellation
Primary output types:
Primary outputs:
- `domain.PreparedRun` from `Prepare`
- `domain.RunResult` from `Run`
- wrapped sentinel errors for adapter mapping
LLM boundary types:
- `domain.GenerateRequest`
- `domain.GenerateResponse`
## Boundaries
## Dependencies
`Runner` coordinates the following interfaces:
`Runner` depends on package interfaces instead of concrete adapter types:
- `promptdef.Repository`
- `profile.Repository`
@@ -34,136 +37,110 @@ LLM boundary types:
- `validate.Validator`
- optional `usecase.OutputRepairer`
Transport concerns (CLI flags, HTTP DTO parsing, status-code mapping) stay outside runner.
The CLI, HTTP adapter, and public Go package construct these dependencies and pass them in.
## Config Fields Used
## Config Fields
`Runner` does not read app config files directly.
`Runner` does not read app config files. Effective behavior is determined by injected dependencies and the `domain.RunRequest`.
It receives fully constructed repositories/readers/validators from adapters. Effective behavior depends on adapter wiring, including:
Adapter wiring commonly reflects these app config fields:
- prompt/profile directories
- schema base directory
- selected profile/runtime overrides in request
- `prompt_dir`
- `profile_dir`
- `schema_dir`
- `server.artifact_root`
- HTTP request/artifact/response size limits
## External Adapters Used
`Runner` works with adapter implementations via interfaces. Current wiring from CLI/HTTP uses:
- filesystem prompt/profile repositories
- composite artifact reader
- Go-template prompt renderer
- OpenAI-compatible LLM client
- standard validator
## State And Resume Behavior
`Runner` is stateless across requests.
- No durable run-state storage.
- No built-in resume/skip checkpoints.
- Each `Run`/`Prepare` executes from request inputs and current repositories.
## Failure Behavior
Primary runner error classes:
- `ErrInvalidRequest`: invalid run request envelope.
- `ErrProfileRequired`: specific invalid-request reason when neither request `profile_id` nor prompt `default_profile` is available.
- `ErrAPIKeyEnvMissing`: specific invalid-request reason when `api_key_env` is set but the named environment variable is unset/empty.
- `ErrPromptLoad`: prompt-definition repository load failures.
- `ErrProfileLoad`: execution-profile repository load failures.
- `ErrArtifactLoad`: artifact read failures.
- `ErrPromptRender`: template render failures.
- `ErrLLMGenerate`: outbound model request failures.
- `ErrValidation`: validation runtime failures (including structured-output schema load/compile failures).
Reason sentinel behavior:
- `ErrProfileRequired` and `ErrAPIKeyEnvMissing` are wrapped with `ErrInvalidRequest`.
- Adapters can use `errors.Is` for stable reason mapping without matching runner prose.
Validation content failures are not run errors:
- `Run` can succeed with `Validation.Status == failed`.
- CLI maps this to exit code `2`.
- HTTP returns `200` with failed validation details.
Runtime model settings are resolved from the selected profile plus request overrides.
## Prepare Flow
`Prepare` performs:
`Prepare`:
1. validate request basics (prompt ID present).
2. load prompt definition by ID/version.
3. select profile ID:
- explicit request profile ID
- prompt `default_profile`
- otherwise return an invalid request with `ErrProfileRequired`
4. load execution profile.
5. merge effective runtime target:
- built-in execution defaults
- selected profile values
- request overrides
- request numeric overrides are presence-aware, so omitted values preserve the current effective value and explicit zero values override it
6. verify credentials when the effective target names `api_key_env`:
- a request-scoped direct API key satisfies the credential requirement
- otherwise a missing/empty env value returns an invalid request with `ErrAPIKeyEnvMissing`
- only the environment-variable name is returned in public output; secret values are never returned
7. resolve output contract and structured-output schema payload when `json_schema` mode is active.
8. read input artifacts.
9. render prompt messages, including any normalized message cache-control metadata.
10. compute prompt/input/render hashes and return `PreparedRun`.
1. requires a non-empty prompt ID.
2. loads the prompt definition and computes its hash.
3. selects the profile from request `profile_id`, then prompt `default_profile`.
4. loads the selected execution profile.
5. merges built-in execution defaults, profile values, and request overrides.
6. applies request-scoped direct API key values for public Go callers.
7. validates endpoint, model, and credential requirements.
8. resolves the output contract and JSON Schema document when required.
9. reads input artifacts.
10. renders prompt messages and hashes the rendered prompt.
11. returns a prepared run without calling the LLM.
`rendered_prompt_hash` includes cache-control metadata when present because it affects the outbound provider request. Prompts without cache control keep the role/content hash behavior.
`Prepare` does not call the LLM.
Runtime target notes:
- Profile `extra_params` and request `extra_params` carry JSON-compatible values through prepared output, run metadata, and `domain.GenerateRequest.Target`.
- The OpenAI-compatible client serializes non-empty `reasoning_effort` as a top-level provider request field.
- The OpenAI-compatible client flattens `extra_params` into provider-specific top-level JSON request fields.
- Empty `extra_params` keys, reserved outbound field names, and values that cannot be JSON-encoded fail before the provider request.
- Resolved API-key values are never serialized in prepared/run output, public results, logs, or HTTP responses.
- Public direct API-key values are carried only far enough to call the configured LLM client and are excluded from JSON/YAML serialization.
Numeric request overrides are presence-aware: omitted values preserve the current effective value, while explicit zero values are real overrides.
## Run Flow
`Run` performs:
`Run`:
1. generate run ID.
2. call `Prepare`.
3. call LLM with prepared messages/effective target/structured-output spec.
4. build output artifact content type from output format.
5. validate output.
6. optionally attempt bounded repair when repairer is injected and contract allows it.
7. return `RunResult` with artifact, raw output, validation, hashes, profile/model metadata, token/cache usage, and timestamps.
1. creates a run ID and start timestamp.
2. calls `Prepare`.
3. calls the injected LLM client with rendered messages, effective target, target presence, and structured-output settings.
4. builds the output artifact.
5. validates the output.
6. optionally attempts bounded repair when a repairer is injected and the contract permits repair.
7. returns the run result with artifact, raw output, validation, hashes, selected profile/model metadata, usage, and timing.
## Repair Hook Boundary
`Run` must reuse `Prepare`; prepare logic should not be duplicated elsewhere.
Repair attempts occur only when all are true:
## Validation And Repair
- repairer is injected
- `repair_attempts > 0`
Validation content failures are returned as successful run results with `Validation.Status == failed`. They are not runtime errors.
Validation runtime failures, such as schema load or compile errors, return `ErrValidation`.
Repair attempts occur only when all conditions are true:
- a repairer is injected
- `repair_attempts` is greater than zero
- validation status is `failed`
- validation mode is `json` or `json_schema`
Current production wiring boundary:
CLI and HTTP wiring call `usecase.NewRunner(...)`, which does not inject a repairer. Normal CLI and HTTP execution therefore does not repair invalid output.
- CLI and HTTP adapters call `usecase.NewRunner(...)` (no repairer argument).
- Therefore normal CLI/HTTP execution does not perform repair attempts today.
## Failure Behavior
## Tests To Inspect Before Changing
Stable runner sentinels include:
- `ErrInvalidRequest`
- `ErrProfileRequired`
- `ErrAPIKeyEnvMissing`
- `ErrAPIKeyRequired`
- `ErrPromptLoad`
- `ErrProfileLoad`
- `ErrArtifactLoad`
- `ErrPromptRender`
- `ErrLLMGenerate`
- `ErrValidation`
Adapters should use `errors.Is` against sentinels and lower-level repository errors instead of matching message text.
Secret values must not appear in prepared output, run results, logs, HTTP responses, or serialized public package results. The effective API-key environment-variable name may appear.
## State And Manifests
The runner is stateless across requests.
- No durable run store.
- No manifest files.
- No checkpoint, skip, or resume behavior.
- Recovery is a new request after correcting inputs, config, or environment.
## Tests To Inspect
- `internal/usecase/runner_test.go`
- `internal/usecase/integration_test.go`
- `engine_test.go`
- `internal/adapter/cli/run_test.go`
- `internal/adapter/http/handler_test.go`
## Architectural Invariants
- `Run` reuses `Prepare`; prepare logic is not duplicated.
- Effective API-key environment-variable name may appear; resolved secret value must not.
- Structured-output schema document must load before LLM call for `json_schema` mode.
- Use-case decisions stay in `internal/usecase`.
- `Run` reuses `Prepare`.
- Prompt/profile/artifact/schema loading remains behind injected boundaries.
- Validation content failures are result state; validation runtime failures are errors.
- Repair loops are bounded by `repair_attempts` and repairer presence.
- Runner stays transport-agnostic.
- Resolved secret values are never serialized or emitted.