Files
scriptorium/docs/internal/runner.md

4.5 KiB

Runner Internals

Purpose

internal/usecase.Runner is the prompt-execution orchestrator. It prepares domain requests, invokes an injected LLM client, validates output, and returns domain results. Transport parsing, response mapping, and public type conversion remain outside this package.

The configuration reference owns prompt, profile, schema, and runtime-setting definitions. Public error behavior is defined by the HTTP API and Go package contracts.

Dependencies And Construction

Runner receives these collaborators:

  • promptdef.Repository;
  • profile.Repository;
  • artifact.Reader;
  • prompt.Renderer;
  • llm.Client;
  • validate.Validator; and
  • an optional OutputRepairer.

NewRunner constructs a runner without a repairer. NewRunnerWithRepairer accepts one explicitly. Adapters and the public engine choose concrete repositories and readers; the runner does not load application configuration.

Prepare Flow

Prepare performs one deterministic preparation pass for a request:

  1. validate the prompt ID and load the prompt definition;
  2. hash the definition and select the explicit or default profile;
  3. load the profile and resolve effective execution settings;
  4. validate endpoint, model, and credential availability;
  5. resolve the output contract and, for JSON Schema output, load a structured schema document before model execution;
  6. read and hash input artifacts;
  7. render messages and the session ID; and
  8. return a PreparedRun containing the effective state and rendered-prompt hash.

Execution settings merge defaults, profile values, and a request override. Numeric override presence is retained so explicit zero values are not confused with omissions.

Run And Validation Flow

Run creates a run ID and timestamps, then calls Prepare rather than duplicating preparation. It sends the prepared prompt, effective target, target-presence state, and optional structured-output specification to the LLM client. It converts the returned content to an output artifact, validates it, and returns the artifact, validation, hashes, usage, and timing metadata.

A validator can return a content result or an operational error. Content failures stay in the result; schema loading, compilation, and validator operational failures are returned as ErrValidation. The canonical distinction for callers is documented by the public contracts.

Repair Boundary

Repair is an internal optional loop. It starts only when a repairer is present, the output contract permits one or more attempts, validation failed, and the validation mode is JSON or JSON Schema. Each repair receives the previous output, validation errors, effective target, structured-output specification, and attempt metadata; every repaired result is validated again.

NewDefaultOutputRepairer delegates to the injected LLM client. CLI, HTTP, and the public engine use NewRunner and therefore do not inject this repairer.

Error Translation

Runner sentinels identify failure categories for adapters:

  • ErrInvalidRequest
  • ErrProfileRequired
  • ErrAPIKeyEnvMissing and ErrAPIKeyRequired
  • ErrPromptLoad, ErrProfileLoad, and ErrArtifactLoad
  • ErrPromptRender
  • ErrLLMGenerate
  • ErrValidation

Wrap errors with those sentinels and preserve their identities through errors.Is; adapters must not classify errors by message text. The runner passes direct keys only to the LLM boundary and never includes resolved key values in prepared or run results.

Package-Local Guarantees

  • Run always reuses Prepare.
  • Schema documents are loaded before the initial LLM call when structured output is required.
  • Output validation records attempts used, including repair attempts.
  • Runner state is per request; the package does not create a durable run store or manifest.
  • Source, renderer, validator, and LLM implementations remain injected boundaries.

Verification And Change Recipe

Inspect:

  • internal/usecase/runner_test.go
  • internal/usecase/integration_test.go
  • engine_test.go

When changing orchestration:

  1. identify the collaborator boundary and the affected Prepare or Run state;
  2. preserve the Run-through-Prepare path and error identity;
  3. add focused runner or integration tests for changed state transitions, validation, or repair behavior; and
  4. update the owning external contract and any affected source or LLM internal document.

The testing policy owns global test sufficiency.