Document Scriptorium as a Promptkit application

This commit is contained in:
2026-07-28 14:21:19 +00:00
parent fb0b21c51d
commit 7bb4cf35b9
18 changed files with 327 additions and 1031 deletions

View File

@@ -1,12 +1,15 @@
# scriptorium
# Scriptorium
Scriptorium is a narrow prompt-execution application for rendering prompt
requests, running them against OpenAI-compatible chat-completions endpoints, and
serving the same run workflow over HTTP.
Scriptorium is a prompt-execution application with a command-line interface and
an HTTP service. It prepares prompt requests, runs them against
OpenAI-compatible model endpoints, and returns generated output with validation
metadata.
It keeps prompt definitions, execution profiles, schemas, and input artifacts as
separate files so prompts can be reviewed and reused without baking model
runtime settings into application code.
The application uses
[Promptkit v0.1.0](https://gitea.maximumdirect.net/eric/promptkit/src/tag/v0.1.0/)
for prompt, profile, schema, preparation, generation, and validation behavior.
Scriptorium owns executable configuration, CLI and HTTP mapping, process
behavior, output presentation, and HTTP artifact-containment policy.
## Quickstart
@@ -21,8 +24,9 @@ go run ./cmd/scriptorium render \
--format json
```
This command renders the prepared prompt and effective runtime settings without calling an LLM.
For complete invocation and output behavior, see the [CLI reference](docs/cli.md).
This renders the prepared prompt and effective runtime settings without calling
a model. For complete invocation and output behavior, see the
[CLI reference](docs/cli.md).
## Documentation
@@ -31,15 +35,16 @@ For complete invocation and output behavior, see the [CLI reference](docs/cli.md
- [HTTP API reference](docs/api.md)
- [Operations guide](docs/operations.md)
- [Consumer integration overview](docs/consumers/api.md)
- [Go library package](docs/consumers/pkg-scriptorium.md)
- [Subprocess integration](docs/integrations/subprocess.md)
- [OpenAI-compatible chat integration](docs/integrations/openai-compatible-chat.md)
- [Architecture policy](docs/policy/architecture.md)
- [Promptkit framework formats](https://gitea.maximumdirect.net/eric/promptkit/src/tag/v0.1.0/docs/formats.md)
- [Promptkit Go consumer guide](https://gitea.maximumdirect.net/eric/promptkit/src/tag/v0.1.0/docs/consumers/pkg-promptkit.md)
## Examples
- [Minimal configuration](examples/config.yml) and [complete configuration](examples/config.full.yml)
- [Prompt definitions](examples/prompts/), [execution profiles](examples/profiles/), [schemas](examples/schemas/), and [synthetic input fixtures](examples/fixtures/)
- [Minimal configuration](examples/config.yml) and
[complete configuration](examples/config.full.yml)
- [Prompt definitions](examples/prompts/), [execution profiles](examples/profiles/),
[schemas](examples/schemas/), and [synthetic input fixtures](examples/fixtures/)
- [Render script](examples/render-markdown-summary.sh)
- [HTTP request](examples/http-run.json)
- [Go library example](examples/go-library/prepare/main.go)

View File

@@ -66,11 +66,12 @@ untrusted writers.
The optional `model` object accepts `endpoint`, `model`, `temperature`,
`max_tokens`, `top_p`, `timeout_seconds`, `service_tier`,
`reasoning_effort`, `api_key_env`, and `extra_params`. Numeric ranges and
credential supply are defined by the [configuration reference](config.md).
framework credential semantics are defined by the
[Promptkit format reference](https://gitea.maximumdirect.net/eric/promptkit/src/tag/v0.1.0/docs/formats.md).
Explicit zero values for the numeric fields are overrides; zero
`timeout_seconds` disables the per-generation deadline only, retaining the
request context and configured transport cap. The timeout layers are defined in
the [outbound integration contract](integrations/openai-compatible-chat.md#authentication-and-timeout).
the [Promptkit outbound integration contract](https://gitea.maximumdirect.net/eric/promptkit/src/tag/v0.1.0/docs/integrations/openai-compatible-chat.md#timeout-and-cancellation).
Raw API-key values are not accepted. `api_key` and any other unknown model
field cause `400 invalid_json`.

View File

@@ -1,7 +1,7 @@
# CLI Reference
This is the canonical contract for invoking Scriptorium. Configuration discovery,
precedence, directories, profiles, and schemas are defined in the
precedence, application source locations, and server settings are defined in the
[configuration reference](config.md). The [HTTP API reference](api.md) owns
service request and response behavior.
@@ -69,7 +69,7 @@ only; the caller context and configured transport cap remain active. CLI
durations are converted to whole seconds by truncation toward zero, so any
duration whose absolute value is below one second becomes an explicit
zero-second override. The timeout layers are defined in the
[outbound integration contract](integrations/openai-compatible-chat.md#authentication-and-timeout).
[Promptkit outbound integration contract](https://gitea.maximumdirect.net/eric/promptkit/src/tag/v0.1.0/docs/integrations/openai-compatible-chat.md#timeout-and-cancellation).
There is no raw API-key flag. Use `--api-key-env`.

View File

@@ -1,15 +1,17 @@
# Configuration Reference
This is the canonical reference for Scriptorium application settings and the
prompt, profile, and schema files those settings select. For command syntax,
see the [CLI reference](cli.md); for HTTP request shapes, limits, and outcomes,
see the [HTTP API reference](api.md).
This is the canonical reference for Scriptorium application settings. Prompt,
profile, schema, execution-setting, built-in profile, and framework credential
semantics are defined by the
[Promptkit v0.1.0 format reference](https://gitea.maximumdirect.net/eric/promptkit/src/tag/v0.1.0/docs/formats.md).
For command syntax, see the [CLI reference](cli.md); for HTTP request shapes and
outcomes, see the [HTTP API reference](api.md).
## Discovery And Precedence
Application settings are resolved in this order:
1. built-in defaults;
1. built-in Scriptorium defaults;
2. a configuration file; then
3. CLI overrides.
@@ -19,7 +21,7 @@ If neither exists, it uses built-in defaults. An explicit `--config` path must
exist and decode successfully.
The maintained [minimal configuration](../examples/config.yml) and
[full configuration](../examples/config.full.yml) are copyable examples.
[complete configuration](../examples/config.full.yml) are copyable examples.
## Application Configuration File
@@ -28,146 +30,57 @@ do not override a prior value. Raw API-key fields are not accepted.
| Field | Default | Meaning |
| --- | --- | --- |
| `prompt_dir` | unset | Directory containing prompt-definition YAML. `run`, `render`, and `serve` require an effective value. |
| `profile_dir` | unset | Directory containing custom profile YAML. Built-in profiles remain available. |
| `schema_dir` | `.` | Base directory for relative JSON Schema paths. |
| `prompt_dir` | unset | Promptkit prompt-definition source directory. `run`, `render`, and `serve` require an effective value. |
| `profile_dir` | unset | Optional custom Promptkit profile source directory overlaid on Promptkit built-ins. |
| `schema_dir` | `.` | Promptkit schema source directory for relative schema paths. |
| `server.addr` | `:8080` | Address used by `serve`. |
| `server.artifact_root` | unset | Root that enables HTTP `file` input references. |
| `server.max_request_bytes` | `16777216` | Maximum encoded HTTP request body bytes; `0` disables the limit. |
| `server.max_request_bytes` | `16777216` | Maximum encoded HTTP request-body bytes; `0` disables the limit. |
| `server.max_artifact_bytes` | `16777216` | Maximum HTTP file-input artifact bytes; `0` disables the limit. |
| `server.max_response_bytes` | `16777216` | Maximum encoded HTTP response bytes; `0` disables the limit. |
| `defaults.render_format` | `text` | Default `render` output format: `text` or `json`. |
| `defaults.render_format` | `text` | Default prepared-run output format: `text` or `json`. |
The three size fields must be zero or greater. The HTTP contract defines how
each limit is enforced and reported. `server.artifact_root` configures the
deployment boundary; see the [HTTP API reference](api.md) for request-path and
containment behavior, and [operations](operations.md) for deployment handling.
The size fields must be zero or greater. The [HTTP API](api.md) defines how
each limit is enforced and reported. `server.artifact_root` configures an HTTP
deployment boundary; see [operations](operations.md) for deployment handling.
## Prompt Definition Files
## Framework Source Mapping
Prompt definitions are strict YAML files anywhere below `prompt_dir`. A prompt
is selected by its YAML `id`, not by file path; nested directories are only for
organization. See [maintained prompt examples](../examples/prompts/).
Scriptorium passes `prompt_dir`, `profile_dir`, and `schema_dir` to Promptkit
when constructing its engine. Scriptorium does not redefine or independently
parse those framework file formats.
| Field | Required | Meaning |
| --- | --- | --- |
| `id` | yes | Prompt identifier. |
| `version` | yes | Prompt version. |
| `default_profile` | no | Profile used when a request omits a profile ID. |
| `description` | no | Human-readable description. |
| `session_id` | no | Go-template string rendered from request variables and sent to a compatible provider when non-empty. |
| `inputs` | no | Declared input metadata. |
| `messages` | yes | Chat-message templates. |
| `output` | yes | Output format and validation contract. |
- Prompt selection, versions, message templates, inputs, output contracts, and
session IDs are Promptkit contracts.
- Profile fields, numeric ranges, execution defaults, overlay precedence,
built-in profiles, and credential rules are Promptkit contracts.
- Schema path behavior and generated-content validation are Promptkit
contracts.
### Inputs And Messages
See the
[tagged Promptkit format reference](https://gitea.maximumdirect.net/eric/promptkit/src/tag/v0.1.0/docs/formats.md)
for all of those definitions. The files under
[`examples/prompts`](../examples/prompts/),
[`examples/profiles`](../examples/profiles/), and
[`examples/schemas`](../examples/schemas/) are maintained Scriptorium
application inputs using that tagged format.
Each `inputs` item has a required `name` and optional `required`,
`content_type`, and `description` fields. Input names must be unique.
## Credentials And Outbound Behavior
Each message has a required `role`, exactly one of `content` or `content_file`,
and optional `cache_control`. A `content_file` path is relative to the prompt
file. `cache_control.type` must be `ephemeral`; its optional `ttl` is `1h`.
Scriptorium maps `--api-key-env` and HTTP `model.api_key_env` into Promptkit
request overrides. Keep secret values in environment variables and store only
their names in configuration or framework source files. Do not place raw keys
in configuration, prompts, profiles, CLI arguments, examples, or HTTP
payloads.
`session_id` uses the same template variables as messages. Empty rendered
values are omitted. A rendered value may contain at most 256 Unicode code
points.
### Output Contract
| Field | Required | Values or behavior |
| --- | --- | --- |
| `format` | yes | `text`, `markdown`, or `json`. |
| `validation_mode` | yes | `none`, `basic`, `json`, or `json_schema`. |
| `schema_path` | for `json_schema` | Schema path, relative to `schema_dir` unless absolute. |
| `repair_attempts` | no | Integer greater than or equal to `0`; omitted means `0`. |
## Profile Definition Files
Profiles are strict YAML files anywhere below `profile_dir`. A profile is
selected by YAML `id`; nested directories are organizational. See the
[maintained profile examples](../examples/profiles/).
| Field | Required | Meaning |
| --- | --- | --- |
| `id` | yes | Profile identifier. |
| `endpoint` | yes | OpenAI-compatible base URL, including its API version path when needed. |
| `model` | yes | Provider model name. |
| `temperature` | no | Number from `0` through `2`. |
| `max_tokens` | no | Integer zero or greater. |
| `top_p` | no | Number from `0` through `1`. |
| `timeout_seconds` | no | Per-generation-call deadline in whole seconds; integer zero or greater. |
| `service_tier` | no | Non-empty provider-specific request tier. |
| `reasoning_effort` | no | Non-empty provider-specific reasoning setting. |
| `api_key_env` | no | Environment-variable name containing the API key. |
| `extra_params` | no | JSON-compatible provider-specific outbound request fields. |
Execution defaults before profile and request overrides are `temperature: 0`,
`max_tokens: 0`, `top_p: 1`, and `timeout_seconds: 600`. Profile numeric values
merge by non-zero value. Request overrides preserve presence, so an explicit
zero can override a profile value. For `timeout_seconds`, explicit request zero
disables the generation deadline while retaining the caller context and the
built-in client's transport cap. See the
[OpenAI-compatible integration contract](integrations/openai-compatible-chat.md#authentication-and-timeout)
for the complete timeout interaction.
Custom profiles take precedence over built-ins with the same ID. Invalid custom
profiles are errors; they do not fall back to a built-in profile. Raw `api_key`
is rejected. Use `api_key_env`, or the public Go package's request-scoped key
mechanism described in the [package contract](consumers/pkg-scriptorium.md).
`extra_params` keys must be non-empty and cannot be `model`, `session_id`,
`messages`, `temperature`, `max_tokens`, `top_p`, `service_tier`,
`reasoning_effort`, or `response_format`.
### Built-In Profile Catalog
Each embedded profile uses `OPENROUTER_API_KEY`.
| Provider | ID | Model |
| --- | --- | --- |
| aion-labs | `aion-2` | `aion-labs/aion-2.0` |
| anthropic | `claude-fable-latest` | `~anthropic/claude-fable-latest` |
| anthropic | `claude-haiku-latest` | `~anthropic/claude-haiku-latest` |
| anthropic | `claude-opus-latest` | `~anthropic/claude-opus-latest` |
| anthropic | `claude-sonnet-latest` | `~anthropic/claude-sonnet-latest` |
| deepseek | `deepseek-3-2` | `deepseek/deepseek-v3.2` |
| deepseek | `deepseek-4-flash` | `deepseek/deepseek-v4-flash` |
| deepseek | `deepseek-4-pro` | `deepseek/deepseek-v4-pro` |
| google | `gemini-2-flash` | `google/gemini-2.5-flash` |
| google | `gemini-2-flash-lite` | `google/gemini-2.5-flash-lite` |
| google | `gemini-2-pro` | `google/gemini-2.5-pro` |
| google | `gemini-3-flash-lite` | `google/gemini-3.1-flash-lite` |
| google | `gemini-flash-latest` | `~google/gemini-flash-latest` |
| google | `gemini-pro-latest` | `~google/gemini-pro-latest` |
| google | `gemma-4-31b` | `google/gemma-4-31b-it:exacto` |
| minimax | `minimax-m2` | `minimax/minimax-m2.5` |
| minimax | `minimax-m3` | `minimax/minimax-m3` |
| mistral | `mistral-large-2512` | `mistralai/mistral-large-2512` |
| mistral | `mistral-medium-3-5` | `mistralai/mistral-medium-3-5` |
| mistral | `mistral-small-3` | `mistralai/mistral-small-3.2-24b-instruct` |
| mistral | `mistral-small-4` | `mistralai/mistral-small-2603` |
| nvidia | `nemotron-3-ultra` | `nvidia/nemotron-3-ultra-550b-a55b` |
| openai | `gpt-5-mini` | `openai/gpt-5.4-mini` |
| openai | `gpt-5-nano` | `openai/gpt-5.4-nano` |
## Schemas
Schemas are JSON files, normally below `schema_dir`. `json_schema` output
requires a `schema_path`. Relative paths resolve from `schema_dir`; absolute
paths are used directly. Referenced nested schemas use relative paths and are
not discovered by basename. An unreadable or invalid schema is a runtime
validation error; generated content that fails JSON or schema validation is a
validation result.
## Credentials
Keep secrets in environment variables. Store only an environment-variable name
in `api_key_env`; do not place raw keys in configuration, prompt or profile
files, CLI arguments, examples, or HTTP payloads.
Promptkit's
[OpenAI-compatible integration contract](https://gitea.maximumdirect.net/eric/promptkit/src/tag/v0.1.0/docs/integrations/openai-compatible-chat.md)
defines outbound authentication, provider request mapping, transport limits,
and timeout layering.
## Related References
- [CLI reference](cli.md)
- [HTTP API reference](api.md)
- [OpenAI-compatible outbound contract](integrations/openai-compatible-chat.md)
- [Operations guide](operations.md)
- [Promptkit framework formats](https://gitea.maximumdirect.net/eric/promptkit/src/tag/v0.1.0/docs/formats.md)

View File

@@ -1,45 +1,21 @@
# Consumer Integration Overview
This guide helps applications choose a Scriptorium interface and understand
their responsibilities. The linked contracts own interface syntax and wire
semantics.
Scriptorium exposes executable interfaces. Choose between a local subprocess
and the HTTP service according to the boundary your application needs.
| Interface | Use when |
| --- | --- |
| Go package | The consumer is Go and needs typed requests, results, or an injected LLM client. |
| CLI subprocess | The consumer needs process isolation or is not written in Go. |
| CLI subprocess | The consumer needs a synchronous local process boundary or prepared output. |
| HTTP API | The consumer needs a service boundary or remote access. |
- Go package: [package contract](pkg-scriptorium.md)
- CLI subprocess: [subprocess integration](../integrations/subprocess.md)
- HTTP service: [HTTP API reference](../api.md)
- Prompt, profile, schema, and credential configuration: [configuration reference](../config.md)
- Application configuration: [configuration reference](../config.md)
## Minimal Go Use
```go
engine, err := scriptorium.NewEngine(scriptorium.Config{
PromptDir: "./examples/prompts",
ProfileDir: "./examples/profiles",
})
if err != nil {
return err
}
prepared, err := engine.Prepare(ctx, scriptorium.RunRequest{
PromptID: "generic.markdown_summary",
Inputs: map[string]scriptorium.ArtifactRef{
"transcript": scriptorium.File("./examples/fixtures/transcript.md"),
},
})
if err != nil {
return err
}
_ = prepared
```
For a maintained program, see
[`examples/go-library/prepare`](../../examples/go-library/prepare).
Go applications that need an in-process prompt framework should import
Promptkit directly. The tagged
[Promptkit Go consumer guide](https://gitea.maximumdirect.net/eric/promptkit/src/tag/v0.1.0/docs/consumers/pkg-promptkit.md)
owns that interface; Scriptorium does not provide a Go library package.
## Consumer Responsibilities
@@ -47,13 +23,14 @@ Consumers are responsible for:
- selecting and deploying prompt, profile, and schema assets;
- supplying required inputs and template variables;
- supplying credentials through the applicable interface;
- protecting rendered prompts and generated artifacts as potentially sensitive;
- supplying credentials through the chosen interface;
- protecting rendered prompts and generated artifacts as potentially
sensitive;
- deciding whether validation-failed output is usable; and
- retrying only when another model call is acceptable.
Scriptorium does not persist run state. A retry can produce different output and
can incur another provider request. CLI exit behavior belongs to the
[CLI reference](../cli.md); HTTP status behavior belongs to the
[HTTP API reference](../api.md); package errors and results belong to the
[package contract](pkg-scriptorium.md).
Scriptorium does not persist run state. A retry can produce different output
and can incur another provider request. CLI exits belong to the
[CLI reference](../cli.md), HTTP status behavior belongs to the
[HTTP API reference](../api.md), and framework semantics belong to
[Promptkit v0.1.0](https://gitea.maximumdirect.net/eric/promptkit/src/tag/v0.1.0/docs/formats.md).

View File

@@ -1,203 +0,0 @@
# Package `scriptorium`
Import path:
```go
import "gitea.maximumdirect.net/eric/scriptorium"
```
This is the canonical public Go contract for in-process prompt preparation and
execution. Prompt, profile, and schema file formats are defined in the
[configuration reference](../config.md).
## Engine Construction
`NewEngine(Config, ...Option)` constructs an engine. `Config` has these
fields:
| Field | Meaning |
| --- | --- |
| `PromptDir` | Prompt-definition directory, required unless a prompt source option is supplied. |
| `ProfileDir` | Optional custom profile directory over built-ins. |
| `SchemaDir` | Schema directory; empty uses `.`. |
| `Timeout` | Transport-wide safety cap for the built-in OpenAI-compatible client when `HTTPClient` is absent or has a non-positive timeout. A non-positive value uses the internal ten-minute default. |
| `HTTPClient` | Optional HTTP client for that built-in client. It is cloned; a positive `Timeout` on it is the transport cap and takes precedence over `Config.Timeout`. A non-positive client timeout is treated as unset. |
Nil options are ignored. Invalid construction, including
`WithLLMClient(nil)` and `WithArtifactReader(nil)`, returns an error matching
`ErrInvalidConfig`.
Profile and request `timeout_seconds` values select a per-generation-call
deadline independently of the transport cap. An explicit request override of
zero disables that generation deadline only. The complete interaction with the
caller context is defined in the
[OpenAI-compatible integration contract](../integrations/openai-compatible-chat.md#authentication-and-timeout).
Source options replace their matching directory source:
- prompts: `WithPromptFS(fsys, root)`, `WithPromptFile(path)`;
- profiles: `WithProfileFS(fsys, root)`, `WithProfileFile(path)`, and
`WithProfiles(profiles...)`;
- schemas: `WithSchemaFS(fsys, root)`, `WithSchemaFile(path)`; and
- LLM client: `WithLLMClient(client)`; and
- artifact reader: `WithArtifactReader(reader)`.
`fs.FS` prompt-content and schema paths stay inside their configured roots.
Single-file prompt and profile sources are selected by their YAML `id`, not
their file names. `WithPromptFile` resolves relative `content_file` paths from
the prompt file's directory. `WithSchemaFile` exposes its schema by the schema
file's base name. In-memory profiles take precedence over an explicit or
directory-backed profile source, which in turn takes precedence over built-ins.
File and filesystem sources use the format and credential rules in the
[configuration reference](../config.md).
## Prepare And Run
`Prepare(ctx, request)` resolves the prompt, profile, input artifacts,
validation contract, and rendered messages without calling an LLM.
`Run(ctx, request)` performs that preparation, calls the configured client,
and validates generated content.
```go
engine, err := scriptorium.NewEngine(scriptorium.Config{
PromptDir: "./examples/prompts",
ProfileDir: "./examples/profiles",
})
if err != nil {
return err
}
prepared, err := engine.Prepare(ctx, scriptorium.RunRequest{
PromptID: "generic.markdown_summary",
Inputs: map[string]scriptorium.ArtifactRef{
"transcript": scriptorium.File("./examples/fixtures/transcript.md"),
},
})
if err != nil {
return err
}
_ = prepared.Messages
```
The maintained package example is
[`examples/go-library/prepare`](../../examples/go-library/prepare).
`PreparedRun` exposes prompt, selected-profile, effective-model, output
contract, structured-output, input-hash, rendered-message, and timing
information. It does not include a resolved API key, model output, validation
result, or target-presence metadata.
`RunResult` adds run ID, artifact, raw output, validation, model metadata,
usage, and duration. Generated-content validation failures return a result with
`Validation.Status == ValidationFailed`; schema or validator runtime failures
return an error matching `ErrValidation`.
## Public Values
`ArtifactRef` has `Type`, `URI`, and `Body`; `Artifact` has `Name`,
`ContentType`, `Body`, `URI`, `Size`, and `Hash`. `ExecutionTarget` exposes the
effective endpoint, model, numeric settings, credential-environment name,
service tier, reasoning effort, and extra parameters. `ValidationResult`
contains status, mode, errors, schema path, repair attempts, and validity.
The exported constants define these serialized values:
- artifact types: `inline` and `file`;
- output formats: `text`, `markdown`, and `json`;
- validation modes: `none`, `basic`, `json`, and `json_schema`; and
- validation statuses: `passed`, `failed`, and `skipped`.
`TokenUsage` reports prompt, completion, total, cached, and cache-write token
counts. `RenderedPrompt`, `RenderedMessage`, `CacheControl`, and
`StructuredOutputSpec` are the public shapes used by injected LLM clients.
`ArtifactReader` implements
`Read(context.Context, ArtifactRef) (*Artifact, error)`. Supplying it through
`WithArtifactReader` replaces, rather than extends, the engine's default inline
and file reader for every input. Omitting the option retains that default;
`WithArtifactReader(nil)` makes engine construction fail with
`ErrInvalidConfig`.
Reader failures are surfaced as errors matching `ErrArtifactLoad` while
preserving the reader's original error identity for `errors.Is`. A `(nil, nil)`
reader response is also an artifact-load failure. Readers are responsible for
artifact metadata, although the engine assigns the input-map name when the
returned name is empty; readers should not retain or mutate caller values.
## Requests, Inputs, And Overrides
`RunRequest` fields are `PromptID`, `PromptVersion`, `ProfileID`,
`APIKey`, `Inputs`, `Vars`, `Execution`, `Validation`, and
`Metadata`.
Input helpers are:
- `File(path)` for a file-backed artifact;
- `Inline(body)` for inline content; and
- `InlineWithURI(uri, body)` for inline content with URI metadata.
Required declared inputs must be supplied. Template rendering must also resolve
every input name the prompt actually references. Extra entries in `Inputs`
are not rejected solely because they are undeclared.
`ExecutionTargetOverride` supplies endpoint, model, credential-environment,
service-tier, reasoning-effort, and extra-parameter overrides. Its numeric
fields (`Temperature`, `MaxTokens`, `TopP`, and `TimeoutSeconds`) are
pointers so explicit zero values are preserved. `OutputContract` supplies
`Format`, `ValidationMode`, `SchemaPath`, and `RepairAttempts`.
`ExtraParams` accepts JSON-compatible values: strings, booleans, finite
numbers, objects with string keys, arrays or slices, and nil. Unsupported
values, non-string map keys, non-finite floats, and cycles return
`ErrInvalidConfig` for profiles or `ErrInvalidRequest` for request
overrides.
## Profiles And Credentials
`OpenAICompatibleProfile(OpenAICompatibleProfileConfig)` creates an
in-memory `Profile`. Its public fields are `ID`, `Endpoint`, `Model`,
`Temperature`, `MaxTokens`, `TopP`, `TimeoutSeconds`, `ServiceTier`,
`ReasoningEffort`, `APIKeyRequired`, and `ExtraParams`.
`WithProfiles` rejects duplicate IDs in one call.
A direct `RunRequest.APIKey` is request-scoped and takes precedence over
`api_key_env` for the built-in client. It is excluded from JSON output and
from `PreparedRun` and `RunResult`. The package's `String` and
`GoString` methods report only whether a direct key is set. Do not use
reflection-based dumps of request structs, which can bypass that redaction.
## Injected LLM Clients
`LLMClient` implements:
```go
Generate(context.Context, GenerateRequest) (*GenerateResponse, error)
```
Injected clients receive the rendered prompt, effective execution target, numeric
target-presence metadata, optional structured-output specification, and direct
request API key. `GenerateResponse` returns content and `TokenUsage`.
Custom clients should avoid logging raw prompts or credentials.
## Errors
Public methods preserve these sentinel checks through `errors.Is`:
- `ErrInvalidConfig`
- `ErrInvalidRequest`
- `ErrPromptNotFound`
- `ErrProfileNotFound`
- `ErrProfileRequired`
- `ErrPromptLoad`
- `ErrProfileLoad`
- `ErrAPIKeyEnvMissing`
- `ErrArtifactLoad`
- `ErrPromptRender`
- `ErrLLMGenerate`
- `ErrValidation`
`ErrProfileRequired` and `ErrAPIKeyEnvMissing` each also match
`ErrInvalidRequest`, so callers can select either the broad request category or
the specific condition.
For the HTTP interface, see the [HTTP API reference](../api.md).

View File

@@ -1,63 +1,56 @@
# Development
This is the contributor entry point for Scriptorium. Use the task-specific
reading guide below before making changes. Canonical architecture, contracts,
component behavior, and policies remain in their owning documents.
This is the contributor entry point for Scriptorium. Scriptorium is an
application that consumes the public
[Promptkit v0.1.0 package](https://gitea.maximumdirect.net/eric/promptkit/src/tag/v0.1.0/docs/consumers/pkg-promptkit.md);
framework implementation work belongs in Promptkit.
## Initial Orientation
Before starting work:
1. inspect the working tree and preserve unrelated changes;
2. read the architecture policy for code or design work;
3. read the policy, contract, and internal documents listed for the task;
4. inspect the relevant implementation and tests before deciding how to change
them.
2. read the [architecture policy](policy/architecture.md);
3. follow the task-specific contracts and internal documents below; and
4. inspect the relevant implementation and tests before changing them.
Start with:
- [Architecture policy](policy/architecture.md) for system boundaries,
invariants, and non-goals;
- [Internal component overview](internal/overview.md) for the current package
and component map;
- [Documentation policy](policy/documentation.md) before changing
documentation;
- [Testing policy](policy/testing.md) before adding, rewriting, or deleting
tests.
Also read the [documentation policy](policy/documentation.md) before changing
documentation and the [testing policy](policy/testing.md) before changing
tests.
## Task-Specific Reading Guide
| Task | Read before changing |
| --- | --- |
| Repository orientation or component responsibility | [Internal component overview](internal/overview.md) and [architecture policy](policy/architecture.md) |
| Public Go package or engine behavior | [Go package consumer contract](consumers/pkg-scriptorium.md), [internal component overview](internal/overview.md), [runner internals](internal/runner.md), [adapter internals](internal/adapters.md), and [source internals](internal/sources.md) |
| CLI commands, flags, output, or exit behavior | [CLI contract](cli.md), [internal component overview](internal/overview.md), and [adapter internals](internal/adapters.md) |
| HTTP routes, DTOs, limits, or status mapping | [HTTP API contract](api.md), [internal component overview](internal/overview.md), [adapter internals](internal/adapters.md), and [source internals](internal/sources.md) |
| Application configuration | [Configuration contract](config.md), [internal component overview](internal/overview.md), [adapter internals](internal/adapters.md), and [source internals](internal/sources.md) |
| Prompt, profile, schema, or artifact loading | [Configuration contract](config.md), [internal component overview](internal/overview.md), and [source internals](internal/sources.md) |
| Runner orchestration, rendering, validation, or repair | [Runner internals](internal/runner.md) and [source internals](internal/sources.md) |
| OpenAI-compatible request or response behavior | [OpenAI-compatible integration](integrations/openai-compatible-chat.md), [LLM internals](internal/llm.md), [runner internals](internal/runner.md), and [adapter internals](internal/adapters.md) |
| CLI commands, flags, output, or exit behavior | [CLI contract](cli.md) and [adapter internals](internal/adapters.md) |
| HTTP routes, DTOs, limits, status mapping, or artifact policy | [HTTP API contract](api.md), [adapter internals](internal/adapters.md), and [source internals](internal/sources.md) |
| Application configuration or precedence | [Configuration contract](config.md), [adapter internals](internal/adapters.md), and [source internals](internal/sources.md) |
| Prepared-run presentation | [CLI contract](cli.md), [adapter internals](internal/adapters.md), and `internal/format` |
| Prompt, profile, schema, generation, or validation semantics | [Promptkit framework formats](https://gitea.maximumdirect.net/eric/promptkit/src/tag/v0.1.0/docs/formats.md) and the [Promptkit consumer guide](https://gitea.maximumdirect.net/eric/promptkit/src/tag/v0.1.0/docs/consumers/pkg-promptkit.md) |
| OpenAI-compatible outbound behavior or timeout layering | [Promptkit integration contract](https://gitea.maximumdirect.net/eric/promptkit/src/tag/v0.1.0/docs/integrations/openai-compatible-chat.md) |
| Subprocess behavior | [Subprocess integration](integrations/subprocess.md) and [CLI contract](cli.md) |
| Runtime operation or recovery | [Operations](operations.md) |
| Examples or copyable assets | The owning contract for the demonstrated behavior and the related files under `examples/` |
| Architecture decisions or future work | The [documentation policy](policy/documentation.md), relevant accepted ADRs such as [ADR 0001](adr/0001-adopt-canonical-documentation-ownership.md), and relevant roadmap documents under `roadmap/` |
| Release packaging | `.woodpecker/release.yml`, [operations](operations.md), and the architecture policy |
| Examples or copyable assets | The owning Scriptorium contract, the relevant [Promptkit format contract](https://gitea.maximumdirect.net/eric/promptkit/src/tag/v0.1.0/docs/formats.md), and the related files under `examples/` |
| Architecture decisions or future work | The [documentation policy](policy/documentation.md), relevant accepted ADRs, and relevant roadmap documents |
For cross-cutting changes, follow every applicable row. Internal component
documents own detailed subsystem change recipes.
Cross-project changes land and release in Promptkit before Scriptorium adopts
the tagged version. Do not commit a Go workspace, local replacement, vendored
Promptkit source, or an import of a Promptkit `internal` package.
## Baseline Validation
Use focused checks while iterating, then run validation proportionate to the
change and the risks described by the testing policy.
The repository-level baseline for code changes is:
For code changes, run:
```bash
go test ./...
go test -race ./...
go vet ./...
go build ./cmd/scriptorium
```
Documentation-only work does not require the full Go suite unless it changes
commands, examples, generated output, or another behavior that the suite
validates. Always check changed links, paths, examples, and canonical ownership.
Check formatting with `gofmt`, run `git diff --check`, and validate affected
examples and documentation links. Documentation-only work does not require
unrelated new tests, but commands and examples changed by documentation must be
run.

View File

@@ -1,121 +0,0 @@
# OpenAI-Compatible Chat Integration
This is the outbound wire contract for Scriptorium's OpenAI-compatible
chat-completions client.
## Endpoint And Method
Scriptorium uses the request endpoint override when present; otherwise it uses
the configured client base URL. It removes a trailing slash and sends
`POST /chat/completions`.
For example, `http://localhost:8000/v1` becomes
`http://localhost:8000/v1/chat/completions`.
## Request Payload
The payload always contains `model` and rendered `messages`. It additionally
contains these fields when applicable:
| Field | Inclusion |
| --- | --- |
| `session_id` | Non-empty rendered prompt session ID. |
| `temperature` | Non-zero effective value or an explicit zero override. |
| `max_tokens` | Non-zero effective value or an explicit zero override. |
| `top_p` | Non-zero effective value or an explicit zero override. |
| `service_tier` | Any non-empty configured value. |
| `reasoning_effort` | Any non-empty configured value. |
| `response_format` | Structured output is requested. |
| provider-specific fields | Flattened from `extra_params`. |
`service_tier` and `reasoning_effort` are forwarded without a provider value
catalog; the selected backend decides which values it supports.
`extra_params` are top-level JSON fields, not a nested object. Keys cannot be
empty or collide with `model`, `session_id`, `messages`, `temperature`,
`max_tokens`, `top_p`, `service_tier`, `reasoning_effort`, or
`response_format`. Values must be JSON-serializable.
A rendered `session_id` is sent as a top-level JSON field, not as a header.
Empty values are omitted. The maximum length is 256 Unicode code points.
Messages without cache control use string `content`. A message with cache
control uses one text block:
```json
{
"role": "system",
"content": [{
"type": "text",
"text": "rendered text",
"cache_control": {"type": "ephemeral", "ttl": "1h"}
}]
}
```
When the prompt omits cache-control `ttl`, the payload omits `ttl`.
Structured JSON Schema output is sent as:
```json
{
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "schema name",
"strict": true,
"schema": {"type": "object"}
}
}
}
```
## Authentication And Timeout
When a direct request API key is present, Scriptorium sends
`Authorization: Bearer <key>` and does not read `api_key_env`. Otherwise, it
resolves the configured non-empty `api_key_env` at request time and sends the
same header. If neither mechanism supplies a key, it sends no
`Authorization` header.
The transport-wide safety cap is chosen at client construction. A positive
timeout on a supplied `http.Client` takes precedence over a positive
`Config.Timeout`; if neither is positive, the internal ten-minute default is
used. The supplied client is cloned, and zero or negative timeout values are
treated as unset.
Separately, a positive effective `timeout_seconds` creates a deadline for each
outbound generation call. Its value follows the execution-setting hierarchy:
an explicit request override, then a non-zero profile value, then the
600-second framework default. An explicit request override of zero disables
only this generation deadline. Negative values are rejected before a request
is sent.
The complete observable rule is that the earliest caller-context deadline,
transport cap, or positive generation deadline terminates the call. Transport
and cancellation failures retain the generation-error classification.
## Response Subset And Failures
A successful provider response must supply non-empty
`choices[0].message.content`. Scriptorium reads these optional or required
usage fields when present:
- `usage.prompt_tokens`
- `usage.completion_tokens`
- `usage.total_tokens`
- `usage.prompt_tokens_details.cached_tokens`
- `usage.cache_write_tokens`
Missing cache usage is reported as zero. Invalid JSON, an empty choices array,
or empty first-choice content is a malformed provider response. Network and
request-construction failures, non-2xx responses, and malformed responses fail
the outbound call. Provider response bodies are not exposed by this client.
The client does not implement built-in retries, tool calls, top-level
`cache_control`, or multi-request payload modes.
## Related References
Prompt schema preparation and runner orchestration are described in
[runner internals](../internal/runner.md). Prompt and profile configuration is
defined by the [configuration reference](../config.md).

View File

@@ -2,115 +2,86 @@
## Purpose
Adapters translate external inputs into public engine requests and translate
public results or errors back to their interface. They own IO and presentation
mechanics; use-case decisions remain behind the root `scriptorium` facade.
Scriptorium adapters translate executable inputs into Promptkit public requests
and translate Promptkit results or errors back to CLI or HTTP behavior. They
own IO and presentation mechanics, not framework decisions.
External contracts are canonical in the [CLI reference](../cli.md), [HTTP API
reference](../api.md), and [Go package contract](../consumers/pkg-scriptorium.md).
External contracts are canonical in the [CLI reference](../cli.md) and
[HTTP API reference](../api.md). Promptkit's public engine contract is
described by its tagged
[Go consumer guide](https://gitea.maximumdirect.net/eric/promptkit/src/tag/v0.1.0/docs/consumers/pkg-promptkit.md).
## Components And Collaborators
- `cmd/scriptorium` passes process arguments and streams to
`internal/adapter/cli`.
- `internal/adapter/cli` parses commands, resolves application settings through
`internal/config`, constructs the public engine, and owns process output
handling.
- `internal/adapter/http` decodes DTOs, maps them to public run requests,
calls its local public `Runner` interface, and maps public errors and results
to HTTP DTOs.
- The root `scriptorium` package maps its public types and options to internal
collaborators and maps selected internal errors to public sentinels.
- `internal/format` formats public prepared runs for the CLI.
- `internal/adapter/cli` resolves settings through `internal/config`,
constructs `promptkit.Engine`, maps CLI values to `promptkit.RunRequest`,
and owns output files, summaries, and exit codes.
- `internal/adapter/http` strictly decodes request DTOs, maps them to Promptkit
public values, calls its adapter-owned `Runner` interface, and maps results
and errors to HTTP DTOs.
- `internal/format` renders `promptkit.PreparedRun` values as deterministic text
or JSON.
## Wiring Flows
### CLI
The CLI resolves configuration before constructing the public engine. `run`
calls `Engine.Run` with a public request and `render` calls `Engine.Prepare`
with the same request mapping. `serve` constructs the HTTP-owned restricted
artifact reader, injects it with `WithArtifactReader`, passes the resulting
engine directly to the HTTP handler, and starts the server.
`run` calls `promptkit.Engine.Run`; `render` calls
`promptkit.Engine.Prepare`. Both share request mapping for prompt/profile
selection, file inputs, variables, and presence-aware execution overrides.
Omitted framework settings remain zero values so Promptkit resolves its own
defaults.
Parser state records whether numeric runtime values were explicitly supplied.
That presence is carried into `scriptorium.ExecutionTargetOverride`, allowing
the engine to distinguish omitted values from explicit zero overrides.
`serve` constructs Scriptorium's restricted HTTP artifact reader, injects it
with `promptkit.WithArtifactReader`, passes the engine through the HTTP
adapter's consumer-owned `Runner` interface, and starts the server.
### HTTP
The handler first enforces transport limits, strict JSON decoding, and the
minimal request shape. It maps DTO values to public types without deciding
prompt selection, source behavior, or validation semantics. On success it maps
the public result to the response DTO; on failure it uses `errors.Is` over
public framework errors and HTTP-local artifact-policy errors to choose the
public error mapping.
The handler enforces transport limits and strict JSON decoding before mapping
DTOs into `promptkit.RunRequest`, `promptkit.ArtifactRef`, and
`promptkit.ExecutionTargetOverride`. On success it reads Promptkit artifact,
validation, model, usage, and metadata values directly.
The [HTTP API reference](../api.md) owns the route, DTO schema, status codes,
and externally observable limit behavior.
### Public Go Facade
`NewEngine` applies public options, selects filesystem, `fs.FS`, single-file,
or in-memory dependencies, and constructs a runner. The conversion functions
copy maps and slices across the boundary so callers do not receive internal
domain values. The facade maps selected internal errors to the public sentinel
set and keeps direct request API keys out of public results.
Failure mapping uses `errors.Is` against Promptkit's public sentinels and the
HTTP reader's Scriptorium-owned containment and size errors. Wrapped reader
errors preserve their identity through Promptkit's artifact-load boundary.
## Package-Local Guarantees
- Adapters do not embed framework orchestration or source-loading decisions.
- Configuration is resolved before adapter dependency composition.
- CLI and HTTP consume the public engine without a repairer; a repairer remains
available only through explicit internal runner construction.
- DTO conversion preserves explicit numeric-override presence.
- Error mapping matches error identities, not error text.
- No adapter creates durable run state; caller-selected output files are not
application state.
- Adapters contain no copied framework types or orchestration.
- Configuration is resolved before Promptkit engine construction.
- Explicit numeric overrides preserve presence, including zero.
- HTTP DTO and error mapping remains stable and transport-owned.
- Resolved secrets are not serialized or printed.
- No adapter creates durable run state.
## Failure And Verification Boundaries
## Verification
Keep external error payloads concise, preserve strict external decoding, and do
not serialize resolved secret values. Validation content failures remain result
state; runtime failures remain errors for the relevant adapter to map.
Inspect focused tests when changing this area:
Inspect:
- `internal/adapter/cli/run_test.go`
- `internal/adapter/http/handler_test.go`
- `engine_test.go`
- `internal/adapter/http/artifact_reader_test.go`
- `internal/format/prepared_run_test.go`
- `internal/adapter/dependency_test.go`
Run the affected adapter package tests and recheck the relevant canonical
contract. The [testing policy](../policy/testing.md) owns global test
sufficiency guidance.
The adapter tests protect parsing, configuration mapping, output, status
mapping, restricted artifacts, and representative real Promptkit-engine
workflows. The dependency test protects the repository boundary.
## Change Recipes
### Application Configuration Fields
For a CLI or HTTP change:
1. Add the field to the relevant `internal/config` shape and default handling.
2. Parse and validate it, then preserve configuration and CLI-override
precedence while wiring it through its consuming adapter.
3. Add focused configuration and adapter tests for parsing, mapping, and
effective behavior.
4. Update the [configuration contract](../config.md) and any affected external
contract.
1. identify the Scriptorium-owned external contract;
2. map through Promptkit public values without copying framework semantics;
3. add or update the narrow application-owned test;
4. update the canonical Scriptorium contract; and
5. coordinate and tag Promptkit first if a required public capability is
genuinely absent.
### CLI Flags
1. Add the flag to the relevant parser in `internal/adapter/cli/run.go`.
2. Keep command scope and application-configuration precedence intentional.
3. Add or update parser and command tests in
`internal/adapter/cli/run_test.go`.
4. Update the [CLI contract](../cli.md) and affected maintained examples.
### Adapter Capabilities
1. Define or reuse an adapter-local consumer interface with public facade
types when a test seam is needed.
2. Implement translation and IO behavior without moving framework decisions out
of the public engine.
3. Add focused mapping, parsing, and error-behavior tests.
4. Update this document and the affected public or integration contract. Update
[source internals](sources.md) when source-loading behavior changes.
Update [source internals](sources.md) when application source locations or HTTP
artifact containment changes.

View File

@@ -1,86 +0,0 @@
# LLM Internals
## Purpose
`internal/llm` defines the provider-neutral `Client` interface and the
OpenAI-compatible client implementation. The [OpenAI-compatible integration
contract](../integrations/openai-compatible-chat.md) owns the outbound HTTP wire
format and protocol behavior.
## Construction
`NewOpenAICompatibleClient` validates a non-empty configured base URL, records
an optional default model, and resolves one transport cap. A supplied client
with a positive timeout supplies that cap; otherwise a positive configured
timeout is used, then the internal default.
When callers supply an `http.Client`, construction clones it rather than
mutating the caller's instance. A supplied client with a zero or negative
timeout receives the resolved transport cap in the clone. The client stores the
trimmed base URL, default model, and cloned client.
## Generate Flow
`Generate` receives a `domain.GenerateRequest` from the runner:
1. validate the effective timeout and choose the request endpoint;
2. map the domain request to the internal wire-request representation;
3. validate and flatten extra parameters and encode JSON;
4. derive a child context when the effective generation timeout is positive,
then create the HTTP request with that context;
5. prefer a direct API key, otherwise resolve the configured key environment
variable;
6. execute with the construction-time HTTP client, reject non-success status
responses without returning
provider response bodies; and
7. decode the response subset into `domain.GenerateResponse`.
`openAIChatRequestFromGenerateRequest` is the conversion boundary for effective
model defaults, explicit numeric-presence state, rendered messages, structured
output, and session-ID validation. `openAIChatRequestPayload` protects reserved
fields and JSON encoding before an HTTP call. The external payload shape is
defined only in the [integration contract](../integrations/openai-compatible-chat.md).
## Error Categories
The package uses these internal sentinels:
- `ErrInvalidConfig` for invalid client construction;
- `ErrInvalidRequest` for invalid effective generation input;
- `ErrRequestFailed` for request construction or transport failures;
- `ErrUnexpectedStatus` for non-success HTTP responses; and
- `ErrMalformedResponse` for invalid or incomplete successful-response data.
The runner maps an invalid LLM request to its invalid-request category and
other LLM failures to its generation category. Adapters then apply their public
error contracts.
## Package-Local Guarantees
- The default-model fallback happens before wire encoding.
- Per-generation timeout handling derives a request context; it never replaces
or mutates the configured HTTP client's transport cap.
- Direct API keys take precedence over environment lookup within this client.
- Provider response bodies are discarded for non-success status responses.
- The client does not implement retries, tool calls, or a stateful session
store.
## Verification And Change Recipe
Inspect:
- `internal/llm/openai_compatible_client_test.go`
- `internal/usecase/runner_test.go`
- `internal/adapter/http/handler_test.go`
When changing the client:
1. keep domain-to-wire mapping inside `internal/llm` and preserve the `Client`
interface;
2. test construction, timeout selection, mapping, and error categorization;
3. update the [OpenAI-compatible integration contract](../integrations/openai-compatible-chat.md)
for any observable wire or protocol change; and
4. update [runner internals](runner.md) if the client boundary or structured
output handoff changes.
The [testing policy](../policy/testing.md) owns global test sufficiency.

View File

@@ -1,48 +1,18 @@
# Internal Component Overview
## Purpose
This is the inventory of Scriptorium's implemented components for contributors.
The [architecture policy](../policy/architecture.md) owns normative boundaries
and invariants; public behavior belongs in the linked contracts.
## Public And Command Entrypoints
This is the complete inventory of Scriptorium's implemented Go components.
The [architecture policy](../policy/architecture.md) owns normative boundaries;
public behavior belongs in the linked contracts.
| Component | Implemented responsibility | References |
| --- | --- | --- |
| Root package `scriptorium` | Public Go facade that constructs the engine, exposes request/result types and options, and maps internal errors. | [Go package contract](../consumers/pkg-scriptorium.md), [adapter internals](adapters.md) |
| `cmd/scriptorium` | Process entrypoint that delegates command execution to the CLI adapter. | [CLI contract](../cli.md), [adapter internals](adapters.md) |
| `cmd/scriptorium` | Process entrypoint that delegates arguments and streams to the CLI adapter. | [CLI contract](../cli.md), [adapter internals](adapters.md) |
| `internal/adapter/cli` | Parses commands, resolves application settings, constructs Promptkit engines, maps requests, and owns process output and exit behavior. | [CLI contract](../cli.md), [adapter internals](adapters.md) |
| `internal/adapter/http` | Owns routes, DTOs, strict decoding, limits, Promptkit request/result mapping, public error mapping, and restricted HTTP artifact reading. | [HTTP API](../api.md), [adapter internals](adapters.md), [source internals](sources.md) |
| `internal/config` | Discovers and strictly decodes application configuration and applies built-in and CLI precedence. | [configuration contract](../config.md), [adapter internals](adapters.md) |
| `internal/defaults` | Holds Scriptorium-owned application and HTTP defaults. | [configuration contract](../config.md) |
| `internal/format` | Formats Promptkit prepared-run values for CLI text or JSON output. | [CLI contract](../cli.md), [adapter internals](adapters.md) |
## Adapters, Domain, And Use Case
| Component | Implemented responsibility | References |
| --- | --- | --- |
| `internal/adapter/cli` | Parses CLI commands, constructs the public engine from application settings, and handles process input and output. | [CLI contract](../cli.md), [adapter internals](adapters.md) |
| `internal/adapter/http` | Maps HTTP requests and responses through public engine values, maps public errors, and owns restricted HTTP artifact policy. | [HTTP API contract](../api.md), [adapter internals](adapters.md) |
| `internal/domain` | Defines core request, result, output-contract, and LLM-boundary types. | [runner internals](runner.md) |
| `internal/usecase` | Implements `Runner` preparation, execution, validation coordination, and the repairer boundary. | [runner internals](runner.md) |
## Configuration And Sources
| Component | Implemented responsibility | References |
| --- | --- | --- |
| `internal/config` | Loads application settings, applies defaults, and applies CLI overrides. | [configuration contract](../config.md), [adapter internals](adapters.md) |
| `internal/defaults` | Holds compile-time default values used when application settings are resolved. | [configuration contract](../config.md) |
| `internal/promptdef` | Loads prompt definitions from filesystem and `fs.FS` sources. | [configuration contract](../config.md), [source internals](sources.md) |
| `internal/profile` | Loads filesystem and `fs.FS` execution profiles and combines profile repositories. | [configuration contract](../config.md), [source internals](sources.md) |
| `internal/profile/builtin` | Provides embedded built-in execution profiles as a repository. | [configuration contract](../config.md), [source internals](sources.md) |
| `internal/filecatalog` | Provides shared YAML discovery and source-root helpers. | [source internals](sources.md) |
| `internal/artifact` | Provides the framework's ordinary inline and unrestricted file artifact reader. | [configuration contract](../config.md), [source internals](sources.md) |
| `internal/prompt` | Renders prompt templates into messages. | [runner internals](runner.md) |
## Formatting, Validation, And Model Access
| Component | Implemented responsibility | References |
| --- | --- | --- |
| `internal/format` | Formats public prepared-run information for CLI output. | [CLI contract](../cli.md), [adapter internals](adapters.md) |
| `internal/validate` | Defines validation interfaces and provides standard filesystem and `fs.FS` schema validation. | [configuration contract](../config.md), [source internals](sources.md), [runner internals](runner.md) |
| `internal/llm` | Defines the provider-neutral LLM client boundary and its OpenAI-compatible implementation. | [OpenAI-compatible integration](../integrations/openai-compatible-chat.md), [LLM internals](llm.md), [runner internals](runner.md) |
Focused internal documents describe the components that have detailed
orchestration, adapter, or source behavior. Package tests live alongside the
implementation and are identified in those focused documents where relevant.
Framework implementation packages are provided by
[Promptkit v0.1.0](https://gitea.maximumdirect.net/eric/promptkit/src/tag/v0.1.0/docs/consumers/pkg-promptkit.md)
and are not part of this repository.

View File

@@ -1,120 +0,0 @@
# 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](../config.md) owns prompt, profile, schema, and
runtime-setting definitions. Public error behavior is defined by the
[HTTP API](../api.md) and [Go package](../consumers/pkg-scriptorium.md)
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. The public engine chooses concrete repositories and
readers; executable adapters reach the runner only through that engine. 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. The public
engine, and therefore CLI and HTTP, uses `NewRunner` and does 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`
- `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](../policy/testing.md) owns global test sufficiency.

View File

@@ -2,98 +2,67 @@
## Purpose
This document describes how source packages load prompt definitions, profiles,
schemas, and artifacts. The [configuration reference](../config.md) owns their
user-facing formats and settings. The [HTTP API reference](../api.md) owns
HTTP-visible artifact outcomes; [operations](../operations.md) owns deployment
handling.
This document covers Scriptorium-owned source locations and the restricted HTTP
artifact reader. Prompt, profile, schema, and ordinary artifact semantics are
owned by the tagged
[Promptkit format reference](https://gitea.maximumdirect.net/eric/promptkit/src/tag/v0.1.0/docs/formats.md).
## Prompt Definitions
## Application Source Locations
`internal/promptdef` provides filesystem and `fs.FS` repositories. Both use
`internal/filecatalog` for recursive YAML discovery, deterministic ordering,
display paths, and root cleaning.
`internal/config` resolves `prompt_dir`, `profile_dir`, and `schema_dir` from
Scriptorium defaults, configuration files, and CLI overrides.
`internal/adapter/cli` passes those paths into `promptkit.Config` when
constructing the engine.
Repositories select a prompt by YAML ID and optional version rather than by
path. They decode through strict YAML handling, reject duplicate matching
definitions, and resolve `content_file` relative to the definition. The `fs.FS`
implementation resolves content paths inside its source root; absolute paths and
traversal outside that root are rejected before file access.
Scriptorium does not search, parse, validate, or overlay framework source files
itself. Promptkit owns prompt selection, profile built-ins and overlays, schema
resolution, ordinary file artifacts, and the related error identities.
## Profiles And Built-Ins
The [configuration reference](../config.md) owns Scriptorium's source-location
fields and precedence. Maintained files under `examples/` are application
inputs that use Promptkit's tagged formats.
`internal/profile` provides filesystem, `fs.FS`, and overlay repositories.
`internal/profile/builtin` exposes embedded assets through the same repository
interface.
## Restricted HTTP Artifact Reader
An overlay asks its primary source first. It falls back only when the primary
reports `ErrProfileNotFound`; invalid YAML, duplicate IDs, validation failures,
and raw-key failures are returned rather than hidden by fallback. This makes a
custom ID override a built-in ID while retaining errors in the custom source.
`internal/adapter/http` implements `promptkit.ArtifactReader` for HTTP
requests. The `serve` path injects it with
`promptkit.WithArtifactReader`, replacing Promptkit's ordinary reader for
inbound HTTP inputs.
The public engine can overlay in-memory profiles ahead of both file-backed and
built-in repositories. Profile field definitions, validation ranges, and the
built-in catalog remain in the [configuration reference](../config.md).
The reader:
## Schemas
- accepts inline references without an artifact root;
- denies file references when no root is configured;
- resolves relative paths below the configured root;
- accepts absolute paths only when they are lexically within that root;
- rejects lexical traversal outside the root;
- applies the configured file byte limit, with zero meaning unlimited;
- preserves content type, body, size, hash, name, and URI metadata; and
- honors context cancellation.
`internal/validate` supplies `StandardValidator` for filesystem sources and
`FSValidator` for `fs.FS` sources. Directory-backed validation loads the named
schema path; it does not search directories by basename. `fs.FS` schema paths
are cleaned and checked against their configured root, while a single-file
source matches its file base name.
Containment is lexical and does not resolve symlinks. The operating system
follows symlinks after the check. The [HTTP API](../api.md) owns observable
request outcomes, and [operations](../operations.md) owns safe deployment
permissions and root selection.
The runner requests a schema document before generation when it needs
structured output. JSON and schema mismatches in generated content are
validation results; source access, decoding, registration, and compilation
failures are operational errors.
## Artifacts
`internal/artifact` owns the framework's ordinary inline and unrestricted file
reader. The public engine uses it by default and permits consumers to replace it
for every input through the public `ArtifactReader` extension. The
HTTP adapter owns its restricted reader for HTTP containment: `serve` injects
that reader into the public engine with `WithArtifactReader`.
The rooted reader cleans paths and applies lexical containment without resolving
symlinks. It checks relative references against the configured root and accepts
absolute references only when they remain inside that lexical root. The OS still
follows symlinks after that check. The public containment outcome is documented
by the [HTTP API reference](../api.md); deployment permissions belong in
[operations](../operations.md).
## Failure Boundaries
Source packages report repository, decoding, duplicate, validation, and read
failures to their callers. They do not select public status codes or response
schemas. The runner categorizes source failures and the public engine preserves
the corresponding public error identities; adapters map those identities to
their own external contract.
Source reads use current filesystem or `fs.FS` content for each request. These
packages create no manifests, checkpoints, or durable run state.
Reader errors remain identifiable after Promptkit wraps them as artifact-load
failures, allowing the HTTP adapter to preserve Scriptorium status and error
codes.
## Verification And Change Recipe
Inspect:
- `internal/promptdef/repository_test.go`
- `internal/profile/repository_test.go`
- `internal/profile/builtin/repository_test.go`
- `internal/artifact/reader_test.go`
- `internal/config/config_test.go`
- `internal/adapter/cli/run_test.go`
- `internal/adapter/http/artifact_reader_test.go`
- `internal/validate/standard_validator_test.go`
- `engine_test.go`
- `internal/adapter/http/handler_test.go`
When updating prompt, profile, schema, or built-in assets:
When changing an application source location or HTTP artifact policy:
1. keep assets valid for the strict loader and the relevant source boundary;
2. update the [configuration reference](../config.md) when a file-format,
catalog, or default changes;
3. run focused source and integration tests, including the built-in repository
test when embedded assets change; and
4. update this document when discovery, precedence, containment, or failure
mechanics change.
The [testing policy](../policy/testing.md) owns global test sufficiency.
1. preserve strict configuration precedence and the Promptkit public boundary;
2. keep containment and size policy in Scriptorium;
3. update focused configuration, reader, and handler tests;
4. update the [configuration](../config.md), [HTTP](../api.md), and
[operations](../operations.md) contracts as applicable; and
5. do not duplicate Promptkit loaders, formats, or ordinary artifact behavior.

View File

@@ -7,8 +7,11 @@ recovery for Scriptorium. It does not redefine invocation syntax, configuration
fields, or HTTP wire behavior.
- [CLI reference](cli.md): commands, output destinations, and exit codes.
- [Configuration reference](config.md): configuration, prompt/profile/schema
formats, defaults, and credentials.
- [Configuration reference](config.md): application settings, source
locations, defaults, and credential mapping.
- [Promptkit framework formats](https://gitea.maximumdirect.net/eric/promptkit/src/tag/v0.1.0/docs/formats.md):
prompt, profile, schema, execution-setting, and framework credential
contracts.
- [HTTP API reference](api.md): route, request/response schema, status codes,
limits, and HTTP artifact access.
- [Consumer integration overview](consumers/api.md): caller responsibilities.
@@ -26,10 +29,10 @@ responsibilities.
## Deploy The Filesystem And Process
Provide the process with readable prompt, profile, and schema sources. Keep
prompt templates adjacent to the prompt definitions that reference them. For an
HTTP deployment that accepts file artifacts, use a dedicated, narrow artifact
directory rather than a general-purpose or sensitive filesystem tree.
Provide the process with readable configured Promptkit prompt, profile, and
schema sources that follow the tagged framework formats. For an HTTP deployment
that accepts file artifacts, use a dedicated, narrow artifact directory rather
than a general-purpose or sensitive filesystem tree.
Run Scriptorium under an identity that can:

View File

@@ -1,83 +1,102 @@
# Architecture
This document is the development architecture policy for Scriptorium.
It is for developers and LLM coding agents. User-facing behavior belongs in `README.md` and the docs under `docs/` that target operators/users.
This document defines Scriptorium's current application architecture and
durable development boundaries.
## System Shape
Scriptorium is a narrow prompt-execution application with three executable
entry paths: CLI `run`, CLI `render`, and the HTTP service started by `serve`.
It also provides a public Go package for in-process use. Executable adapters
consume framework behavior through that public facade; the facade continues to
compose the framework implementation inside this single repository. Its current
component inventory is maintained in the [internal overview](../internal/overview.md).
Scriptorium is an executable application with three entry paths: CLI `run`, CLI
`render`, and the HTTP service started by `serve`. It does not expose a reusable
root Go package.
Domain behavior is centralized in `internal/usecase` and `internal/domain`.
The application consumes
[Promptkit v0.1.0](https://gitea.maximumdirect.net/eric/promptkit/src/tag/v0.1.0/docs/consumers/pkg-promptkit.md)
through its supported root package. Promptkit owns prompt execution,
preparation, source formats, built-in profiles, model-client behavior, and
validation. Scriptorium owns application configuration, executable adapters,
prepared-run presentation, process behavior, and HTTP deployment policy.
## Core Principles
- Keep orchestration narrow: Scriptorium executes one prompt request; it is not a multi-step workflow engine.
- Keep adapter logic thin: adapters map external shapes to public engine
requests/results and should not hold framework decisions.
- Keep boundaries explicit: repositories/loaders/renderers/validators/LLM client stay behind package interfaces.
- Keep external decoding strict: configuration, prompt, and profile YAML and
HTTP JSON should reject unknown fields.
- Keep secrets out of payloads: raw API key values must not be accepted or emitted.
The concrete package inventory is maintained in the
[internal overview](../internal/overview.md).
## Dependency Direction
- Adapters translate external shapes and IO concerns; they do not make
use-case decisions.
- Executable adapters and prepared-run formatting use the public facade for
framework behavior rather than importing framework implementation packages
directly.
- Use-case and domain code depend on explicit repository, renderer, validator,
and LLM interfaces rather than adapter implementations.
- Source, rendering, validation, and LLM implementations remain behind their
package boundaries.
- Dependency-specific types must not leak across unrelated package boundaries.
- Prefer the standard library; add an external dependency only when it
materially reduces risk or complexity.
```text
cmd/scriptorium
|
v
CLI and HTTP adapters, configuration, defaults, and formatting
|
v
gitea.maximumdirect.net/eric/promptkit
```
## State And Persistence Policy
- Retained application packages may import Promptkit's root package.
- They must not import Promptkit `internal` packages.
- They must not import the removed Scriptorium root facade or recreate former
framework package families.
- Adapter-owned interfaces use Promptkit public values when a consumer-side
substitution boundary is needed.
- Scriptorium passes omitted framework settings as zero values so Promptkit
applies its own defaults.
Scriptorium has no durable run-state store.
The repository architecture guard enforces these import and removal
invariants.
- No built-in resume/checkpoint/archive behavior.
- Recovery model is rerun after correcting inputs/config/environment.
## Retained Boundaries
## Contract Ownership
- `internal/adapter/cli` owns commands, flags, configuration precedence,
process streams, output files, summaries, and exit codes.
- `internal/adapter/http` owns routes, strict JSON DTOs, size limits, response
mapping, status mapping, and the restricted artifact reader.
- `internal/config` owns discovery and strict decoding of Scriptorium
application configuration.
- `internal/defaults` owns Scriptorium application and HTTP defaults only.
- `internal/format` owns deterministic prepared-run text and JSON presentation.
- Promptkit owns framework orchestration and contracts. Its
[format reference](https://gitea.maximumdirect.net/eric/promptkit/src/tag/v0.1.0/docs/formats.md)
and
[outbound integration contract](https://gitea.maximumdirect.net/eric/promptkit/src/tag/v0.1.0/docs/integrations/openai-compatible-chat.md)
are canonical.
The [CLI](../cli.md), [configuration](../config.md), [HTTP API](../api.md),
[public Go package](../consumers/pkg-scriptorium.md), and
[integration](../integrations/) documents own their respective external
contracts. This policy keeps only the architectural boundaries that govern
their implementation.
## HTTP Artifact Security Boundary
## Error Handling And Logging
Ordinary CLI file loading is provided by Promptkit. Scriptorium's HTTP adapter
injects a restricted `promptkit.ArtifactReader` for inbound HTTP requests.
That reader denies file references without an artifact root, enforces the
configured byte limit, and applies Scriptorium's lexical root-containment rule.
The operating system still follows symlinks after the lexical check.
- Wrap errors with domain/operation context.
- Map public error identities to adapter-appropriate statuses/codes without
leaking sensitive internals.
- Never emit raw secret values.
The [HTTP API](../api.md) owns observable request outcomes, and
[operations](../operations.md) owns deployment permissions and root selection.
## Testing And Documentation
## State, Errors, And Secrets
Testing philosophy and change-validation expectations are defined by the
[testing policy](testing.md). Documentation ownership and maintenance rules are
defined by the [documentation policy](documentation.md).
Scriptorium has no durable run-state store, checkpoint, cache, or resume
mechanism. Recovery is a new request after correcting inputs, configuration, or
environment.
Adapters map Promptkit public error identities into CLI exits or HTTP statuses
without classifying by message text. Raw API keys are not accepted in
Scriptorium configuration, CLI arguments, or HTTP payloads, and resolved
secrets must not be emitted.
## Architectural Invariants
- `Runner.Run` reuses `Runner.Prepare` flow.
- Raw API key values must not be accepted through external configuration or
request payloads, and resolved secret values must not be emitted.
- External YAML and JSON decoding remains strict.
- CLI and HTTP behavior remains presentation and transport logic rather than
framework orchestration.
- Explicit numeric request overrides preserve presence, including zero.
- HTTP artifact containment and byte limits remain Scriptorium policy.
- No application package depends on Promptkit implementation packages.
## Non-Goals
- Do not move orchestration responsibilities from external callers into Scriptorium.
- Do not add adapter-specific business logic in `internal/adapter/*` packages.
- Do not bypass repository/renderer/validator/LLM boundaries by introducing cross-package coupling.
- Do not recreate an in-process Scriptorium framework API or compatibility
facade.
- Do not copy Promptkit types, defaults, built-in profiles, or implementation
into Scriptorium.
- Do not move CLI, inbound HTTP, process, or deployment policy into Promptkit.
- Do not add durable workflow, archive, or resume behavior.
Work that is not implemented belongs in `docs/roadmap/`.

View File

@@ -69,11 +69,11 @@ secret values.
| Documentation organization | `docs/policy/documentation.md` | Documentation ownership, audience boundaries, maintenance rules, and ADR/document lifecycle. | Application architecture or product behavior. |
| Testing policy | `docs/policy/testing.md` | Test philosophy, risk-based sufficiency, test boundaries, doubles, coverage guidance, regression-test policy, and criteria for adding, rewriting, or deleting tests. | Subsystem behavior, application contracts, subsystem-specific test inventories, and implementation plans. |
| CLI contract | `docs/cli.md` | Commands, arguments, flags, invocation semantics, and exit codes. | End-to-end operating procedures, configuration field definitions, runtime filesystem layout, module implementation details. |
| Configuration contract | `docs/config.md` | Discovery and precedence, file schema, fields, defaults, environment overrides, validation rules, and user-selectable module or validator keys. | Complete example files, CLI syntax, runtime state lifecycle, module implementation details. |
| Configuration contract | `docs/config.md` | Application discovery and precedence, source locations, server fields, render default, HTTP limits, and credential mapping. | Promptkit framework formats and defaults, complete example files, CLI syntax, runtime lifecycle, and implementation detail. |
| Operations | `docs/operations.md` | Runtime workflows, physical filesystem and state layout, output, cache, and debug handling, resume, cleanup, permissions, recovery, and operational limits. | CLI flag syntax, configuration field definitions, logical output schemas, implementation mechanics. |
| Public HTTP contract | `docs/api.md` | Routes, authentication, media types, request and response schemas, status codes, pagination, caching, idempotency, rate limits, and HTTP retry semantics. | Client walkthroughs, upstream or downstream integration internals, implementation detail. |
| Consumer guidance | `docs/consumers/` | Task-oriented use of the public interface, minimal client examples, and consumer responsibilities. | HTTP wire semantics, external protocol contracts, internal implementation detail. |
| External and durable integration contracts | `docs/integrations/` | External file formats and protocols, upstream and downstream contracts, logical output bundle paths and schemas, media types, and compatibility behavior. | Physical runtime placement and lifecycle, internal transformations, CLI syntax, configuration defaults. |
| Consumer guidance | `docs/consumers/` | Choosing between Scriptorium's executable interfaces and understanding consumer responsibilities. | HTTP wire semantics, CLI syntax, Promptkit's Go package, and internal implementation detail. |
| External and durable integration contracts | `docs/integrations/` | Scriptorium-owned process and executable integration contracts. | Promptkit framework formats and outbound provider protocols, physical runtime placement, internal transformations, CLI syntax, and configuration defaults. |
| Implemented component inventory | `docs/internal/overview.md` | Current packages and components, their implemented responsibilities, and links to focused internal docs. | Normative architecture, contributor reading policy, external contracts. |
| Internal component behavior | Other files under `docs/internal/` | Implementation flow, internal collaborators and state transitions, package-local guarantees and failures, and relevant tests. | Global architecture invariants, configuration definitions and defaults, external schemas, operator procedures. |
| Architectural decision history | `docs/adr/` | Significant decisions, context, alternatives, rationale, consequences, and supersession history. | Current behavior reference, implementation status, task sequencing. |

View File

@@ -77,7 +77,7 @@ Test through the narrowest stable boundary that expresses the behavior clearly.
This is often the package API, but it may instead be:
- a smaller pure function when dense domain logic is most clearly isolated there;
- a smaller pure function when dense application logic is most clearly isolated there;
- a package-level operation when several internal collaborators jointly produce the behavior; or
- a larger integration boundary when correctness emerges from interaction with a real dependency.
@@ -162,13 +162,16 @@ Use a test-controlled limit and measure the behavior relative to that limit. Do
Each behavior should have a clear test owner.
- Parser tests own parsing cases.
- Validator tests own validation rules.
- Domain tests own transformations and invariants.
- Adapter tests own external integration behavior.
- Orchestrator tests own coordination and failure propagation.
- CLI tests own argument and configuration mapping.
- End-to-end tests prove that representative assembled workflows work.
- Configuration tests own application YAML, discovery, precedence, and
application defaults.
- CLI tests own argument mapping, streams, summaries, exit behavior, and
representative command workflows.
- HTTP tests own DTOs, strict decoding, limits, status mapping, and restricted
artifact policy.
- Formatter tests own prepared-run text and JSON presentation.
- Architecture tests own dependency direction and removal invariants.
- Promptkit owns framework parsing, orchestration, validation, profiles, and
model-client behavior.
Higher-level tests should not repeat every lower-level case. A single intentional policy change should not require unrelated edits across many test files.
@@ -221,7 +224,9 @@ Coverage is a diagnostic, not a target.
Use it to find untested critical branches and unexpectedly weak packages. Do not write low-value tests solely to increase a percentage, and do not infer test quality from coverage alone.
Pure domain logic will often warrant higher coverage than CLI wiring or external adapters. Uneven coverage is acceptable when it reflects risk.
Security-sensitive HTTP containment and external mappings may warrant denser
coverage than straightforward process wiring. Uneven coverage is acceptable
when it reflects risk.
Increasing coverage is valuable only when the newly covered behavior protects a meaningful risk at an acceptable cost.

View File

@@ -231,9 +231,9 @@ passes its documented validation, and has published its first versioned tag
before Scriptorium or another consumer adopts it, as required by
[ADR 0003](../adr/0003-use-maintainer-run-validation-and-tag-only-releases-for-promptkit.md).
**Gate status:** Complete as of 2026-07-28. The
[Step 6 completion record](step6.md) records source Scriptorium commit
`c7263ab2a8e58f7fb97280082d327a820c7cece7`, accepted Promptkit commit
**Gate status:** Complete as of 2026-07-28. Repository history records source
Scriptorium commit `c7263ab2a8e58f7fb97280082d327a820c7cece7`,
accepted Promptkit commit
`9e68a2bbf779545995270c47842048a3bc6c85dc`, independently passing acceptance,
published annotated tag `v0.1.0`, and successful remote-consumer validation.
Scriptorium remains unchanged at its pre-cutover boundary. Step 7 adoption of