Complete the public facade adapter boundary
This commit is contained in:
@@ -113,11 +113,16 @@ counts. `RenderedPrompt`, `RenderedMessage`, `CacheControl`, and
|
||||
|
||||
`ArtifactReader` implements
|
||||
`Read(context.Context, ArtifactRef) (*Artifact, error)`. Supplying it through
|
||||
`WithArtifactReader` replaces the engine's default inline and file reader for
|
||||
all inputs. Reader errors remain available through `errors.Is` alongside
|
||||
`ErrArtifactLoad`; a nil artifact with no error is treated as an artifact-load
|
||||
failure. Readers must supply artifact metadata and should not retain or mutate
|
||||
the caller's values.
|
||||
`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
|
||||
|
||||
@@ -191,5 +196,8 @@ Public methods preserve these sentinel checks through `errors.Is`:
|
||||
- `ErrLLMGenerate`
|
||||
- `ErrValidation`
|
||||
|
||||
For interface selection and operational responsibilities, see the
|
||||
[consumer integration overview](api.md).
|
||||
`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).
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
## Purpose
|
||||
|
||||
Adapters translate external inputs into domain requests, compose dependencies,
|
||||
and translate domain results or errors back to their interface. They own IO and
|
||||
presentation mechanics; use-case decisions remain in `internal/usecase`.
|
||||
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.
|
||||
|
||||
External contracts are canonical in the [CLI reference](../cli.md), [HTTP API
|
||||
reference](../api.md), and [Go package contract](../consumers/pkg-scriptorium.md).
|
||||
@@ -14,35 +14,37 @@ reference](../api.md), and [Go package contract](../consumers/pkg-scriptorium.md
|
||||
- `cmd/scriptorium` passes process arguments and streams to
|
||||
`internal/adapter/cli`.
|
||||
- `internal/adapter/cli` parses commands, resolves application settings through
|
||||
`internal/config`, constructs a runner, and owns process output handling.
|
||||
- `internal/adapter/http` decodes DTOs, maps them to `domain.RunRequest`, calls
|
||||
a runner interface, and maps errors and results to HTTP DTOs.
|
||||
`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 prepared runs for the CLI; `internal/llm`,
|
||||
`internal/prompt`, and source packages supply runner dependencies.
|
||||
- `internal/format` formats public prepared runs for the CLI.
|
||||
|
||||
## Wiring Flows
|
||||
|
||||
### CLI
|
||||
|
||||
The CLI resolves configuration before constructing dependencies. `run` builds a
|
||||
runner with the ordinary composite artifact reader and invokes `Runner.Run`;
|
||||
`render` uses the same wiring and invokes `Runner.Prepare`; `serve` replaces the
|
||||
file reader with the restricted artifact reader, builds an HTTP handler, and
|
||||
starts the server.
|
||||
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.
|
||||
|
||||
Parser state records whether numeric runtime values were explicitly supplied.
|
||||
That presence is carried into `domain.ExecutionTargetOverride`, allowing the
|
||||
runner to distinguish omitted values from explicit zero overrides.
|
||||
That presence is carried into `scriptorium.ExecutionTargetOverride`, allowing
|
||||
the engine to distinguish omitted values from explicit zero overrides.
|
||||
|
||||
### HTTP
|
||||
|
||||
The handler first enforces transport limits, strict JSON decoding, and the
|
||||
minimal request shape. It maps DTO values to domain types without deciding
|
||||
minimal request shape. It maps DTO values to public types without deciding
|
||||
prompt selection, source behavior, or validation semantics. On success it maps
|
||||
the domain result to the response DTO; on failure it uses `errors.Is` over
|
||||
runner, source, artifact, and profile errors to choose the public error mapping.
|
||||
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 [HTTP API reference](../api.md) owns the route, DTO schema, status codes,
|
||||
and externally observable limit behavior.
|
||||
@@ -57,10 +59,10 @@ set and keeps direct request API keys out of public results.
|
||||
|
||||
## Package-Local Guarantees
|
||||
|
||||
- Adapters do not embed runner orchestration or source-loading decisions.
|
||||
- Adapters do not embed framework orchestration or source-loading decisions.
|
||||
- Configuration is resolved before adapter dependency composition.
|
||||
- CLI and HTTP create runners without a repairer; a repairer is available only
|
||||
through explicit internal runner construction.
|
||||
- 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
|
||||
@@ -105,9 +107,10 @@ sufficiency guidance.
|
||||
|
||||
### Adapter Capabilities
|
||||
|
||||
1. Define or reuse the appropriate domain or use-case interface boundary.
|
||||
2. Implement translation and IO behavior without moving use-case decisions out
|
||||
of `internal/usecase`.
|
||||
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.
|
||||
|
||||
@@ -17,8 +17,8 @@ and invariants; public behavior belongs in the linked contracts.
|
||||
|
||||
| Component | Implemented responsibility | References |
|
||||
| --- | --- | --- |
|
||||
| `internal/adapter/cli` | Parses CLI commands, applies application wiring, and handles process input and output. | [CLI contract](../cli.md), [adapter internals](adapters.md) |
|
||||
| `internal/adapter/http` | Maps HTTP requests and responses to domain operations and maps public errors. | [HTTP API contract](../api.md), [adapter internals](adapters.md) |
|
||||
| `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) |
|
||||
|
||||
@@ -32,14 +32,14 @@ and invariants; public behavior belongs in the linked contracts.
|
||||
| `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` | Reads inline and file-backed input artifacts. | [configuration contract](../config.md), [HTTP API contract](../api.md), [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 prepared-run information for CLI output. | [CLI contract](../cli.md), [adapter internals](adapters.md) |
|
||||
| `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) |
|
||||
|
||||
|
||||
@@ -25,8 +25,9 @@ contracts.
|
||||
- an optional `OutputRepairer`.
|
||||
|
||||
`NewRunner` constructs a runner without a repairer. `NewRunnerWithRepairer`
|
||||
accepts one explicitly. Adapters and the public engine choose concrete
|
||||
repositories and readers; the runner does not load application configuration.
|
||||
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
|
||||
|
||||
@@ -68,8 +69,9 @@ validation mode is JSON or JSON Schema. Each repair receives the previous
|
||||
output, validation errors, effective target, structured-output specification,
|
||||
and attempt metadata; every repaired result is validated again.
|
||||
|
||||
`NewDefaultOutputRepairer` delegates to the injected LLM client. CLI, HTTP, and
|
||||
the public engine use `NewRunner` and therefore do not inject this repairer.
|
||||
`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
|
||||
|
||||
@@ -104,7 +106,6 @@ values in prepared or run results.
|
||||
Inspect:
|
||||
|
||||
- `internal/usecase/runner_test.go`
|
||||
- `internal/usecase/integration_test.go`
|
||||
- `engine_test.go`
|
||||
|
||||
When changing orchestration:
|
||||
|
||||
@@ -50,14 +50,11 @@ failures are operational errors.
|
||||
|
||||
## Artifacts
|
||||
|
||||
`internal/artifact` composes inline and file readers. The ordinary composite
|
||||
reader used by CLI and the public engine reads file references from the process
|
||||
filesystem. `internal/adapter/http` provides the restricted public artifact
|
||||
reader for HTTP containment: it combines inline reading with a rooted file
|
||||
reader and optional byte limit. The existing internal restricted composite
|
||||
reader remains a temporary bridge for the current handler and serve wiring; it
|
||||
does not define the HTTP reader's long-term boundary or carry a compatibility
|
||||
promise.
|
||||
`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
|
||||
@@ -70,8 +67,9 @@ by the [HTTP API reference](../api.md); deployment permissions belong in
|
||||
|
||||
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 wraps source failures with use-case categories; adapters map
|
||||
them to their own external contract.
|
||||
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.
|
||||
@@ -86,7 +84,6 @@ Inspect:
|
||||
- `internal/artifact/reader_test.go`
|
||||
- `internal/adapter/http/artifact_reader_test.go`
|
||||
- `internal/validate/standard_validator_test.go`
|
||||
- `internal/usecase/integration_test.go`
|
||||
- `engine_test.go`
|
||||
|
||||
When updating prompt, profile, schema, or built-in assets:
|
||||
|
||||
@@ -8,15 +8,18 @@ It is for developers and LLM coding agents. User-facing behavior belongs in `REA
|
||||
|
||||
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. Its current component
|
||||
inventory is maintained in the [internal overview](../internal/overview.md).
|
||||
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).
|
||||
|
||||
Domain behavior is centralized in `internal/usecase` and `internal/domain`.
|
||||
|
||||
## 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 domain requests/results and should not hold domain decisions.
|
||||
- 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.
|
||||
@@ -26,6 +29,9 @@ Domain behavior is centralized in `internal/usecase` and `internal/domain`.
|
||||
|
||||
- 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
|
||||
@@ -52,7 +58,8 @@ their implementation.
|
||||
## Error Handling And Logging
|
||||
|
||||
- Wrap errors with domain/operation context.
|
||||
- Map domain errors to adapter-appropriate statuses/codes without leaking sensitive internals.
|
||||
- Map public error identities to adapter-appropriate statuses/codes without
|
||||
leaking sensitive internals.
|
||||
- Never emit raw secret values.
|
||||
|
||||
## Testing And Documentation
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
## Status
|
||||
|
||||
Proposed. Implement each stage in order and satisfy its gate before continuing.
|
||||
Complete as of 2026-07-28. All implementation gates, the dependency guard,
|
||||
documentation reconciliation, and required validation passed. Migration Step 5
|
||||
repository creation is the next gate; it was not started here.
|
||||
|
||||
## Objective
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## Status
|
||||
|
||||
Accepted plan. Steps 1 through 3 are complete. Steps 4 through 9 remain
|
||||
Accepted plan. Steps 1 through 4 are complete. Steps 5 through 9 remain
|
||||
proposed and are not yet implemented.
|
||||
|
||||
## Objective
|
||||
@@ -125,8 +125,8 @@ is accepted and records the required ownership and coordination decisions.
|
||||
Strengthen or add contract-focused tests where needed so extraction can be
|
||||
verified without relying on package placement.
|
||||
|
||||
The accepted implementation scope and intended completion state are in the
|
||||
[Step 3 framework-characterization roadmap](step3.md).
|
||||
The completed Step 3 gate records the accepted implementation scope and
|
||||
intended completion state.
|
||||
|
||||
Preserve coverage of:
|
||||
|
||||
@@ -172,6 +172,12 @@ implementations.
|
||||
**Gate:** The CLI and HTTP adapters use only the public framework API for
|
||||
framework behavior, and all tests and documented smoke commands pass.
|
||||
|
||||
**Gate status:** Complete as of 2026-07-28. CLI `run`, `render`, and `serve`,
|
||||
the HTTP handler, and prepared-run formatting use the public facade; the
|
||||
restricted HTTP reader is injected through the public extension point. The
|
||||
dependency guard, full tests, vet, build, race checks, maintained examples, and
|
||||
configuration smoke checks passed. Step 5 repository creation is next.
|
||||
|
||||
### Step 5: Create The Promptkit Repository
|
||||
|
||||
Create the Promptkit repository and Go module as an explicit out-of-band
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
|
||||
## Status
|
||||
|
||||
Proposed. Migration Steps 1 through 3 are complete; this roadmap defines the
|
||||
required target state for Step 4. The separate
|
||||
[implementation plan](implementation.md) defines the ordered work.
|
||||
Complete as of 2026-07-28. The public-facade adapter boundary is established;
|
||||
the [implementation plan](implementation.md) records the completed work, and
|
||||
the [main migration roadmap](migration.md) identifies repository creation as
|
||||
the next gate.
|
||||
|
||||
## Purpose
|
||||
|
||||
@@ -19,31 +20,18 @@ owns the long-term project boundary. The
|
||||
[main migration roadmap](migration.md) owns the overall sequence. This feature
|
||||
roadmap defines the desired Step 4 state, not an implementation sequence.
|
||||
|
||||
## Current Gap
|
||||
## Achieved Boundary
|
||||
|
||||
The public `Engine` facade already supports the ordinary `Prepare` and `Run`
|
||||
workflows, directory and alternate framework sources, injected model clients,
|
||||
public result values, and broad public error classification. The executable
|
||||
adapters do not yet use that boundary consistently:
|
||||
|
||||
- the CLI constructs framework repositories, readers, renderer, validator, and
|
||||
OpenAI-compatible client directly, then calls the internal runner;
|
||||
- the HTTP handler accepts internal domain request and result values;
|
||||
- HTTP error mapping inspects framework-internal sentinels;
|
||||
- the HTTP artifact-containment reader implements the internal artifact-reader
|
||||
interface;
|
||||
- prepared-run formatting accepts an internal domain value; and
|
||||
- adapter tests frequently construct internal runners or use internal domain
|
||||
values.
|
||||
|
||||
Those dependencies would prevent Scriptorium from compiling after the
|
||||
framework packages move to Promptkit. They also allow the executable to exercise
|
||||
a different composition path from downstream Go consumers.
|
||||
The CLI, HTTP handler, and prepared-run formatter now consume public engine
|
||||
values and errors. `serve` injects Scriptorium's HTTP-owned restricted artifact
|
||||
reader through the public extension point, while the root facade continues to
|
||||
compose the framework implementation inside this repository. A repository-level
|
||||
dependency test protects the direct-import boundary.
|
||||
|
||||
## Target State
|
||||
|
||||
At completion, Scriptorium's executable path is an ordinary consumer of the
|
||||
same public framework boundary used by other Go applications:
|
||||
Scriptorium's executable path is an ordinary consumer of the same public
|
||||
framework boundary used by other Go applications:
|
||||
|
||||
```text
|
||||
cmd/scriptorium
|
||||
|
||||
Reference in New Issue
Block a user