# Adapter And Repository Internals ## Purpose This document describes implemented adapter/repository boundaries and their current behavior. ## Adapter Map - `internal/adapter/cli`: CLI command parsing, app wiring, stdout/stderr handling, exit codes. - `internal/adapter/http`: HTTP request/response mapping for `POST /v1/runs`. - root package `scriptorium`: public Go library facade for preparing and running prompt requests. - `internal/promptdef`: filesystem prompt-definition repository. - `internal/profile`: filesystem, `fs.FS`, and overlay execution-profile repositories. - `internal/profile/builtin`: embedded built-in execution-profile repository. - `internal/artifact`: input artifact reader. - `internal/prompt`: Go-template renderer. - `internal/llm`: OpenAI-compatible LLM client implementation. - `internal/validate`: output validator. - `internal/format`: prepared-run formatters for `render` output. ## Inputs And Outputs CLI adapter: - Input: process args, filesystem config/assets, environment. - Output: exit code, stdout artifact/prepared output, stderr summaries/errors. - `run` summaries include cache usage counters only when either parsed cache counter is non-zero. HTTP adapter: - Input: JSON request body (`runRequestDTO`). - Output: JSON success/error body with mapped status codes. - Success metadata includes token usage plus cache usage counters. Public library facade: - Input: typed `scriptorium.RunRequest` values. - Output: typed `PreparedRun` and `RunResult` values plus public sentinel errors. - Custom LLM behavior is injected with `WithLLMClient`; otherwise the default OpenAI-compatible client is used. - Public types are facade types converted at the package boundary; internal domain types remain internal. Filesystem repositories: - Input: prompt/profile YAML files under configured directories. - Output: normalized domain definitions/profiles or typed errors. Profile repository composition: - Built-in profiles are embedded and loaded through the same profile validation rules as filesystem profiles. - When no custom profile directory is configured, the runner receives the built-in profile repository. - When a custom profile directory is configured, the runner receives an overlay repository with custom profiles as primary and built-ins as fallback. - Overlay lookup falls back only after custom profile-not-found errors; custom load/validation/raw-key errors are returned directly. Artifact reader: - Input: `domain.ArtifactRef`. - Output: loaded `domain.Artifact`. LLM adapter: - Input: `domain.GenerateRequest`. - Output: `domain.GenerateResponse`. Validator: - Input: artifact body + output contract. - Output: validation result or runtime validation error. ## Boundaries - Adapters convert external representations to domain requests and back. - Use-case decisions remain in `internal/usecase`. - External dependency details stay scoped to adapter packages. ## Config Fields Used Primary app settings consumed by adapters: - `prompt_dir` - `profile_dir` (optional custom profile source) - `schema_dir` - `server.addr` - `defaults.render_format` Execution profile/request settings used through runner: - `endpoint`, `model`, `temperature`, `max_tokens`, `top_p`, `timeout_seconds`, `service_tier`, `api_key_env`, `reasoning_effort`, `extra_params` - CLI and HTTP request adapters preserve caller intent for numeric runtime overrides. Omitted values remain absent; explicit zero values are mapped as explicit overrides. - HTTP `extra_params` accepts JSON-compatible values and maps them to domain request overrides without provider-specific adapter logic. ## External Dependencies - YAML decoding: `gopkg.in/yaml.v3` (strict known-fields mode in config/prompt/profile loaders). - JSON Schema validation: `github.com/santhosh-tekuri/jsonschema/v6`. - HTTP client/server: Go standard library. ## Failure Behavior Strict decoding and input checks: - config/prompt/profile loaders reject unknown YAML fields. - prompt/profile repositories scan nested subdirectories recursively. - prompt/profile lookup uses YAML `id` values; subdirectory paths are organizational only. - duplicate prompt/profile IDs are invalid and fail instead of using first-match behavior. - duplicate profile IDs across custom and built-in sources are allowed; the custom source overrides the built-in profile. - HTTP DTO decoder rejects unknown JSON fields. - raw API key payload fields are rejected by strict decoding in profile/http paths. Artifact refs: - Supported reference types: `inline`, `file`. - Unsupported types return `ErrUnsupportedRefType`. LLM adapter: - endpoint appends `/chat/completions`. - rendered messages without cache control serialize with string `content`. - rendered messages with cache control serialize as one text content block with `cache_control`. - non-empty `reasoning_effort` serializes as a top-level provider request field. - `extra_params` flatten into provider-specific top-level JSON request fields. - reserved `extra_params` keys are rejected before the provider call: `model`, `session_id`, `messages`, `temperature`, `max_tokens`, `top_p`, `service_tier`, `reasoning_effort`, and `response_format`. - empty `extra_params` keys and values that cannot be JSON-encoded are rejected before the provider call. - compatible cache usage response fields are parsed into domain token usage. - non-2xx responses map to request failure errors. - malformed responses (including missing/empty first choice content) are errors. Validator: - `basic`, `json`, `json_schema` content failures return `ValidationFailed` results. - schema load/compile/path failures are runtime errors. - schema lookup uses explicit `schema_path` values relative to `schema_dir`; it does not recursively search by basename. HTTP error mapping: - maps domain/use-case errors to stable HTTP code + error code/message. - distinguishes missing profile selection and missing `api_key_env` variable using stable use-case sentinel errors. - avoids returning internal wrapped-cause details in response payload. ## CLI Adapter Semantics Implemented commands: - `run` - `render` - `serve` Behavior highlights: - `run` exit `2` indicates validation failed after generation. - `render` does not call the LLM. - `serve` exposes HTTP handler only; no built-in auth. - `render` supports `--format text|json`; `render` does not expose `--schema-dir`. - deprecated aliases `--prompt-id` and `--profile-id` are still accepted. ## Tests To Inspect Before Changing - `internal/adapter/cli/run_test.go` - `internal/adapter/http/handler_test.go` - `internal/promptdef/repository_test.go` - `internal/profile/repository_test.go` - `internal/artifact/reader_test.go` - `internal/prompt/renderer_test.go` - `internal/llm/openai_compatible_client_test.go` - `internal/validate/standard_validator_test.go` - `internal/format/prepared_run_test.go` ## Architectural Invariants - Adapter packages do not own runner decision logic. - External request/response strictness is part of contract stability. - Prepared-render output never includes resolved API key values. - Outbound OpenAI-compatible request includes currently serialized first-class fields (`model`, optional `session_id`, `messages`, optional `temperature`, `max_tokens`, `top_p`, optional `service_tier`, optional `reasoning_effort`, optional `response_format`) plus validated `extra_params` flattened as provider-specific top-level fields. - Outbound cache control is message-level only; no top-level cache-control field is serialized.