Files
promptkit/docs/internal/sources.md

11 KiB

Internal Sources And Validation

Purpose

This document describes Promptkit's implemented configuration, artifact, rendering, and output-validation behavior. The architecture policy owns dependency rules, while the framework format reference owns fields, defaults, and source semantics. Internal packages are not consumer APIs.

Typed Configuration Loading

internal/configuration is the single adapter from ConfigKit's final effective configuration to execution-owned definitions. It performs one layered load, captures effective presence (including explicit null), records normalization and defaults through resolution, and validates all effective backend, profile, and prompt values. Replaced lower-layer domain values are not separately validated. It builds an immutable backend index and presence-aware profile targets, applying final backend defaults only after profile inheritance. It retains ConfigKit's result privately for explicit inspection, not for per-run resolution. The runtime/domain packages do not depend on ConfigKit.

The root DirectoryInputs and FilesystemInputs helpers discover sorted YAML files under an explicit root, provide resource context, and leave imports disabled. Each returned slice belongs to one strict-composition group; file order does not confer priority. The engine prepends selected catalog and Promptkit-owned definitions, then caller underlays and optional main. The configuration tests exercise the real loader and final-value handoff.

Prompt Definitions

The configuration adapter indexes each prompt by exact ID and version, materializes file-backed message content using the exact effective field's origin and resource context, and checks static message/session template syntax at construction. It stores captured content, compiled templates, declared input metadata, and a definition hash. Alongside the unchanged hashed definition, it retains content-type and description presence for each declared input and provides an internal requirement snapshot with those declarations and the captured parses. Metadata values come from the final normalized declaration; absent and null fields remain distinguishable from explicit strings, including empty strings. It closes content-file handles before returning and retains no source capability for public inspection.

Role and cache-control normalization is applied to execution-owned message copies. The retained ConfigKit configuration keeps effective input message metadata and its resource origins: recording a sequence-wide normalization in ConfigKit v0.5.0 would replace origins of unchanged content-file fields.

The runner selects from this index for preparation and InspectPrompt; the internal requirement snapshot lookup selects by exact prompt ID and version. These paths do not rescan configuration or reopen message files. InspectPrompt returns declared metadata without rendering or resolving profile, input, or schema resources, and its public shape and prompt hashes do not include the captured presence state. Runtime rendering still checks request variables and input helpers. The prompt adapter tests cover resource origins, context containment, capture, and selection.

The internal/prompt requirement walker uses the captured parse trees and declaration metadata without executing templates or reading artifacts. It collects literal input names, direct root caller keys, and literal-key index lookups. For if and with, it classifies branches when their conditions are simple supported values: direct values, literals, negation, or two-string equality/inequality. It uses Go template truthiness, so a supplied nonempty string such as false is true. Missing decision values and content-dependent conditions leave dependency-bearing branches conditional and report incomplete discovery; unresolved conditions with no branch dependencies do not by themselves make discovery incomplete. Recognizable references on known skipped paths remain optional inventory entries with an inactive-use explanation, unless another applicable use establishes a stronger classification. Skipped expressions are scanned only for syntactically recognizable names and are not evaluated. A direct caller value or literal-key lookup can also supply an input name, including through simple parentheses or a direct pipeline to input; names are used exactly as supplied. Unresolved names and unsupported dependency-bearing expressions produce structured limitations while retaining recognizable references. Where an unresolved name or scope could affect existing optional entries, those entries are reported as conditional rather than inactive-only or definitively optional. Direct root-map with forms retain caller-map identity. Known scalar values remain separate from unknown scopes; accesses through untracked local maps report incomplete discovery rather than silently losing caller keys. Invoked named templates are scanned conservatively, and dependency-bearing range scopes make discovery incomplete. Uncalled definitions and bypassed session templates are not traversed. This internal walker does not change rendering behavior.

Profiles And Built-Ins

internal/catalog selects the released keyed OpenRouter and Rakestrawhome assets as one ConfigKit underlay and PromptKit-owned profiles as the next. It discovers YAML inputs and supplies passive filesystem context without parsing or pre-validating catalog definitions. The configuration loader validates only the final effective backend and profile values, including consumer overlays of maintained bases or backends. There is no catalog-only preflight, reserved source namespace, or replay of backend registration.

ConfigKit resolves profile inheritance before the handoff. The configuration adapter validates effective references and field values, then builds target snapshots with backend defaults and presence/suppression state. A profile can remain partial until runtime overrides complete it. InspectProfile selects that captured target without prompt, artifact, schema, credential lookup, or provider work. Preparation applies runtime overrides and then checks target completeness. The target tests cover effective target interpretation, while the catalog tests cover selected released assets.

Ordinary Artifacts

internal/artifact accepts explicitly typed inline references even when their body is empty. It also resolves unrestricted, caller-selected paths only when they identify regular operating-system files, checking that condition before and after opening the file. It copies content into an artifact, records metadata and an opaque content-equality value, and applies a content-type fallback.

Regular files are read synchronously in bounded chunks. Cancellation is checked before opening, before and after every read, and before publishing the artifact, so a canceled read never publishes partial content. The ordinary reader does not detach file reads into background goroutines.

This ordinary reader does not implement an inbound HTTP security boundary. In particular, it does not constrain files to an application root or impose an HTTP request-size policy. Scriptorium's restricted HTTP reader remains an application concern outside Promptkit. The artifact reader tests own the implemented reader behavior and failures.

Rendering

internal/prompt renders captured, pre-parsed definition templates using named artifacts and variables. It clones templates to bind request-specific input helpers without mutating shared state. Within one render, each referenced artifact body is converted to text lazily and cached by input name for reuse across the session and every message; the cache is not shared across renders. Conversion uses bounded chunks and preserves the artifact bytes exactly.

Template parsing occurs during engine construction. Runtime execution remains synchronous. The renderer checks cancellation around execution boundaries, between artifact conversion chunks, around each message, and before publishing the complete prompt. It cannot interrupt template work already in progress and never publishes a partial prompt after observing cancellation. It validates and canonicalizes message roles before carrying roles, session IDs, and cache control into the rendered prompt. The renderer tests own rendering behavior.

Schemas And Output Validation

internal/validate provides validators backed by an operating-system filesystem or an fs.FS. Invalid generated content is returned as a validation result; inability to load, register, or compile a schema is an operational error.

Every preparation operation creates one operation-local validation plan. None, basic, and JSON modes retain the effective output contract without source access. JSON Schema mode loads the root document once, resolves and compiles each transitive reference, and retains the compiled validator. Schema compiler resources use canonical escaped file or private-scheme URLs; loaders decode their paths once and enforce the configured source boundary. The provider-facing structured-output metadata uses the root document captured by the same plan.

Schema preparation and execution remain synchronous. Promptkit checks cancellation before and after source resolution, JSON decoding, compilation, and validation, and between bounded schema-read chunks. Once cancellation is observed it returns the context error without publishing a partial plan or validation result, even when a compiler or validator has just returned a different error or a successful result. An fs.FS method or JSON Schema dependency call already in progress cannot be preempted; Promptkit waits for that call to return and then gives cancellation precedence. Validation does not detach dependency work into background goroutines.

Prepare discards its validation plan after returning metadata. Run retains its plan for initial and repaired-output validation, then discards it with the operation. PrepareExecution retains the plan in its private frozen payload; RunPrepared uses that plan without reopening configuration, message, input, or schema resources or rerendering the request. A later ordinary Run uses the same captured configuration but prepares request artifacts and schemas afresh.

The validator tests own basic, JSON, JSON Schema, source resolution, schema loading, compilation, frozen-reference behavior, content-failure behavior, and the synchronous cancellation boundary. Prepared execution orchestration is owned by the use-case tests.