Files
scriptorium/docs/internal/runner.md

4.8 KiB

Runner Internals

Purpose

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.

Transport parsing, DTOs, CLI output, HTTP status mapping, and public package type conversion belong outside the runner.

Inputs And Outputs

Primary inputs:

  • domain.RunRequest
  • repositories/readers/renderers/validators injected at construction
  • context.Context for cancellation

Primary outputs:

  • domain.PreparedRun from Prepare
  • domain.RunResult from Run
  • wrapped sentinel errors for adapter mapping

LLM boundary types:

  • domain.GenerateRequest
  • domain.GenerateResponse

Dependencies

Runner depends on package interfaces instead of concrete adapter types:

  • promptdef.Repository
  • profile.Repository
  • artifact.Reader
  • prompt.Renderer
  • llm.Client
  • validate.Validator
  • optional usecase.OutputRepairer

The CLI, HTTP adapter, and public Go package construct these dependencies and pass them in.

Config Fields

Runner does not read app config files. Effective behavior is determined by injected dependencies and the domain.RunRequest.

Adapter wiring commonly reflects these app config fields:

  • prompt_dir
  • profile_dir
  • schema_dir
  • server.artifact_root
  • HTTP request/artifact/response size limits

Runtime model settings are resolved from the selected profile plus request overrides.

Prepare Flow

Prepare:

  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.

Numeric request overrides are presence-aware: omitted values preserve the current effective value, while explicit zero values are real overrides.

Run Flow

Run:

  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.

Run must reuse Prepare; prepare logic should not be duplicated elsewhere.

Validation And Repair

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

CLI and HTTP wiring call usecase.NewRunner(...), which does not inject a repairer. Normal CLI and HTTP execution therefore does not repair invalid output.

Failure Behavior

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

  • 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.
  • Resolved secret values are never serialized or emitted.