289 lines
14 KiB
Markdown
289 lines
14 KiB
Markdown
# ADR 0002: Split Promptkit From Scriptorium
|
|
|
|
## Status
|
|
|
|
Accepted
|
|
|
|
## Date
|
|
|
|
2026-07-26
|
|
|
|
## Context
|
|
|
|
Scriptorium currently combines two products in one Go module:
|
|
|
|
- a reusable prompt-execution framework with a public Go facade; and
|
|
- a runnable application with CLI and HTTP interfaces.
|
|
|
|
Downstream Go projects increasingly import the framework directly and do not
|
|
use the executable interfaces. Keeping both products in one module couples
|
|
framework releases, dependencies, documentation, and public API evolution to
|
|
application-specific transport concerns.
|
|
|
|
Promptkit will become the framework project, and Scriptorium will become a slim
|
|
application that consumes it. This ADR records that end-state boundary. It does
|
|
not assert that the split has been implemented; until then, the current
|
|
repository structure and contracts remain authoritative.
|
|
|
|
## Decision
|
|
|
|
### Projects And Module Paths
|
|
|
|
Create a repository named `promptkit` alongside Scriptorium:
|
|
|
|
| Project | Repository and Go module path | Root Go package |
|
|
| --- | --- | --- |
|
|
| Promptkit | `gitea.maximumdirect.net/eric/promptkit` | `promptkit` |
|
|
| Scriptorium | `gitea.maximumdirect.net/eric/scriptorium` | No reusable root facade after migration |
|
|
|
|
Promptkit will expose its supported public API from the module root. Its
|
|
implementation packages will remain under `internal/` unless a real consumer
|
|
extension point requires a public type or interface.
|
|
|
|
Scriptorium will import only Promptkit's supported public packages. It will not
|
|
import Promptkit implementation packages or reproduce Promptkit orchestration.
|
|
|
|
### Product Responsibilities
|
|
|
|
Promptkit owns application-neutral framework behavior:
|
|
|
|
- the engine and its `Prepare` and `Run` workflow;
|
|
- public request, result, profile, option, extension, and error APIs;
|
|
- prompt-definition loading and rendering;
|
|
- profile loading, overlays, and the embedded built-in profile registry;
|
|
- schema loading and output validation;
|
|
- provider-neutral model-client boundaries and the OpenAI-compatible client;
|
|
- artifact types, artifact-reader injection, and general-purpose inline and
|
|
caller-selected file readers;
|
|
- execution-setting resolution and framework defaults; and
|
|
- framework-level secret redaction and error classification.
|
|
|
|
Scriptorium owns executable and transport behavior:
|
|
|
|
- the `scriptorium` process and its `run`, `render`, and `serve` commands;
|
|
- CLI parsing, streams, output files, formatting, exit codes, and process
|
|
cancellation behavior;
|
|
- application-configuration discovery and CLI-over-configuration precedence;
|
|
- HTTP routing, strict request decoding, DTO mapping, response encoding,
|
|
status codes, and transport limits;
|
|
- HTTP artifact-root containment and deployment policy;
|
|
- server construction, server defaults, and process logging; and
|
|
- executable release artifacts.
|
|
|
|
The dependency direction is:
|
|
|
|
```text
|
|
Scriptorium CLI and HTTP adapters
|
|
|
|
|
v
|
|
Promptkit public API
|
|
|
|
|
v
|
|
injected sources, readers, and model clients
|
|
```
|
|
|
|
### Current Package Disposition
|
|
|
|
Implementation may reorganize files during extraction, but each current package
|
|
has this target owner:
|
|
|
|
| Current package or file group | Target owner | Disposition |
|
|
| --- | --- | --- |
|
|
| Root `scriptorium` facade files and tests | Promptkit | Move and rename the public package to `promptkit`; Scriptorium retains no compatibility facade. |
|
|
| `internal/domain`, `internal/usecase` | Promptkit | Move as internal engine implementation. |
|
|
| `internal/promptdef`, `internal/prompt` | Promptkit | Move as internal prompt loading and rendering. |
|
|
| `internal/profile`, `internal/profile/builtin` | Promptkit | Move with embedded built-in assets and registry tests. |
|
|
| `internal/filecatalog` | Promptkit | Move as source-loading support. |
|
|
| `internal/validate` | Promptkit | Move as schema and output-validation implementation. |
|
|
| `internal/llm` | Promptkit | Move with the OpenAI-compatible integration. |
|
|
| `internal/artifact` | Split | Move general inline/file reading to Promptkit; keep rooted, denied, and byte-limited HTTP file reading in Scriptorium behind a Promptkit reader interface. |
|
|
| `internal/defaults` | Split | Move framework, execution, output-artifact, content-type, and model-client defaults to Promptkit; keep CLI, HTTP, and server defaults in Scriptorium. |
|
|
| `internal/adapter/cli`, `internal/adapter/http` | Scriptorium | Keep and refactor to use Promptkit's public API. |
|
|
| `internal/config` | Scriptorium | Keep application settings, discovery, validation, and CLI precedence. |
|
|
| `internal/format` | Scriptorium | Keep prepared-run presentation, rewritten against Promptkit public values. |
|
|
| `cmd/scriptorium` | Scriptorium | Keep as the process entrypoint. |
|
|
|
|
Tests move with the behavior they protect. Cross-boundary tests will live with
|
|
the consuming side: Promptkit protects framework contracts, while Scriptorium
|
|
protects adapter mapping, HTTP containment, and executable behavior.
|
|
|
|
### Public Boundary
|
|
|
|
Promptkit's initial facade will preserve the useful shape of the current
|
|
Scriptorium Go API where that reduces extraction risk. It will expose only the
|
|
capabilities required by Promptkit consumers and by Scriptorium:
|
|
|
|
- engine construction, preparation, and execution;
|
|
- public request, result, profile, and error values;
|
|
- prompt, profile, schema, artifact-reader, validator, and model-client source
|
|
or injection options that have demonstrated consumers; and
|
|
- enough stable error identity for Scriptorium to map CLI and HTTP outcomes.
|
|
|
|
Promptkit will not export its domain package, runner implementation,
|
|
repositories, adapter DTOs, or general internal constructors merely to
|
|
simplify the move.
|
|
|
|
Scriptorium's CLI and HTTP adapters will depend on a small consumer-facing
|
|
`Prepare`/`Run` interface where test substitution is needed. That interface
|
|
belongs at the consuming boundary rather than forcing adapter concepts into
|
|
Promptkit.
|
|
|
|
### Artifact Reading And HTTP Containment
|
|
|
|
Promptkit will define the artifact-reader extension point used during
|
|
preparation. Its ordinary file reader may read a path deliberately supplied by
|
|
an in-process or CLI caller and does not claim to be a deployment sandbox.
|
|
|
|
Scriptorium will implement the HTTP-specific reader that:
|
|
|
|
- denies file references when no artifact root is configured;
|
|
- applies the configured artifact byte limit;
|
|
- enforces Scriptorium's documented lexical root-containment rule; and
|
|
- maps reader failures to Scriptorium HTTP error responses.
|
|
|
|
Scriptorium will inject that reader through Promptkit's public construction
|
|
boundary. Promptkit will not know about HTTP roots, status codes, request DTOs,
|
|
or deployment policy.
|
|
|
|
### Configuration And Default Ownership
|
|
|
|
Configuration ownership follows the behavior configured, not the current file
|
|
location:
|
|
|
|
| Configuration category | Owner |
|
|
| --- | --- |
|
|
| Application configuration discovery, configuration-file precedence, `prompt_dir`, `profile_dir`, and `schema_dir` | Scriptorium |
|
|
| CLI flags and their mapping to application settings or request overrides | Scriptorium |
|
|
| `server.*`, render-output settings, HTTP byte limits, and server defaults | Scriptorium |
|
|
| Prompt-definition, profile, and output-contract file formats | Promptkit |
|
|
| Prompt/profile source selection, overlays, schema behavior, and built-in profiles | Promptkit |
|
|
| Execution settings, presence-aware request overrides, and execution defaults | Promptkit |
|
|
| Built-in OpenAI-compatible client settings, timeout behavior, and provider wire mapping | Promptkit |
|
|
| HTTP request and response fields, including their mapping to framework values | Scriptorium |
|
|
|
|
Scriptorium will translate its application settings and external request
|
|
values into Promptkit construction options and requests. When an omitted
|
|
Scriptorium setting means “use the framework default,” Scriptorium will omit
|
|
the override rather than copy Promptkit's numeric default.
|
|
|
|
### Compatibility And Versioning
|
|
|
|
This migration is intentionally breaking:
|
|
|
|
- new Go consumers will import `gitea.maximumdirect.net/eric/promptkit`;
|
|
- Scriptorium will not provide aliases, forwarding wrappers, or deprecated
|
|
compatibility packages for its former Go facade;
|
|
- existing consumers may remain pinned to the final framework-bearing
|
|
Scriptorium tag until migrated; and
|
|
- intermediate migration phases need not preserve source compatibility, but
|
|
each merged phase must be internally buildable and tested.
|
|
|
|
Promptkit's first release will be `v0.1.0`. During the migration, incompatible
|
|
Promptkit changes may advance its minor version until a stable `v1` contract is
|
|
declared. The first slim Scriptorium release will advance the Scriptorium minor
|
|
version beyond the final framework-bearing release. Normal semantic-versioning
|
|
rules apply independently to both projects after the migration.
|
|
|
|
Promptkit must be tagged before Scriptorium or another consumer publishes a
|
|
release that depends on it. Release branches must use tagged module
|
|
dependencies, not local replacements or unpublished revisions.
|
|
|
|
### Local Development And Cross-Repository Coordination
|
|
|
|
For coordinated local work, place both repositories in a temporary Go
|
|
workspace or use an uncommitted module replacement. `go.work`,
|
|
`go.work.sum`, and local filesystem `replace` directives must not be committed
|
|
to release branches.
|
|
|
|
Cross-repository changes follow this order:
|
|
|
|
1. land and tag the required Promptkit capability;
|
|
2. update Scriptorium and other consumers to that tag;
|
|
3. run each repository's own CI and smoke checks; and
|
|
4. release consumers only after the Promptkit tag is available.
|
|
|
|
Migration coordination must confirm out-of-band repository creation, Promptkit
|
|
tags, and downstream migrations before dependent work proceeds.
|
|
Cross-repository changes are coordinated, not treated as atomic commits.
|
|
|
|
### Documentation And Maintained Assets
|
|
|
|
Each repository will maintain its own README, contributor guide, architecture,
|
|
documentation, testing, release, and operations material appropriate to that
|
|
project. Cross-project documents will link to the canonical owner rather than
|
|
copy its contract.
|
|
|
|
Existing documentation and maintained assets have these target owners:
|
|
|
|
| Current material | Target owner |
|
|
| --- | --- |
|
|
| Current README and executable quickstart | Scriptorium; Promptkit creates its own framework orientation |
|
|
| Public Go package and Go-consumer guidance | Promptkit |
|
|
| Prompt, profile, schema, execution-setting, and framework credential reference | Promptkit |
|
|
| OpenAI-compatible integration contract and framework internal documents | Promptkit |
|
|
| CLI, HTTP API, subprocess, and Scriptorium operations contracts | Scriptorium |
|
|
| Consumer interface overview | Scriptorium, revised to route Go consumers to Promptkit |
|
|
| Application-configuration discovery, server settings, and adapter internals | Scriptorium |
|
|
| Current internal overview and source documentation | Split into repository-local overviews; Promptkit owns framework sources and Scriptorium owns HTTP containment |
|
|
| This ADR and cross-project migration records | Scriptorium |
|
|
| `examples/go-library` | Promptkit |
|
|
| `examples/config*.yml`, `examples/render-markdown-summary.sh`, and `examples/http-run.json` | Scriptorium |
|
|
| Example prompts, profiles, schemas, and synthetic fixtures used by the executable examples | Scriptorium |
|
|
| Embedded built-in profile assets | Promptkit |
|
|
| Scriptorium release workflow and executable packaging | Scriptorium |
|
|
| Repository-level license, ignore rules, agent guidance, and development policies | Each repository maintains its own applicable copy |
|
|
|
|
Promptkit will create or retain its own minimal framework examples and test
|
|
fixtures rather than making either repository's tests depend on the other's
|
|
working tree. Scriptorium's framework-format documentation will become a short
|
|
version-appropriate link to Promptkit, while its maintained executable examples
|
|
remain self-contained.
|
|
|
|
## Alternatives Considered
|
|
|
|
- Keep the current combined repository and improve package naming. This avoids
|
|
migration work but retains release and ownership coupling between the
|
|
framework and executable.
|
|
- Add Promptkit as a wrapper around the Scriptorium public package. This gives
|
|
consumers a new import path but leaves framework ownership and dependency
|
|
direction inverted.
|
|
- Extract Promptkit while retaining a Scriptorium compatibility facade. This
|
|
reduces immediate consumer changes but creates a second public API surface
|
|
and prolongs duplicate maintenance.
|
|
- Move all artifact reading into Promptkit. This would place HTTP containment,
|
|
byte limits, and deployment policy in the application-neutral framework.
|
|
- Keep Promptkit and Scriptorium as separate modules in one repository. This
|
|
separates imports but not repository permissions, release workflows,
|
|
issue ownership, or independent project evolution.
|
|
|
|
## Rationale
|
|
|
|
A separate Promptkit project makes the reusable framework the direct owner of
|
|
the API that downstream Go projects already consume. Keeping Scriptorium as a
|
|
public-API consumer exercises the same boundary as other consumers and prevents
|
|
its adapters from relying on framework internals.
|
|
|
|
The selected split keeps transport and deployment policy close to the
|
|
Scriptorium interfaces that expose it, while allowing Promptkit to remain
|
|
useful to in-process consumers with different IO and security requirements.
|
|
Explicit package, configuration, documentation, and asset ownership reduces
|
|
ambiguity during extraction and after release.
|
|
|
|
## Consequences
|
|
|
|
- All Go consumers of the framework must change their import path.
|
|
- Promptkit and Scriptorium gain independent issue, release, CI, policy, and
|
|
documentation lifecycles.
|
|
- Scriptorium becomes a real downstream integration test of Promptkit's public
|
|
facade.
|
|
- Framework changes that affect Scriptorium require tagged, ordered
|
|
cross-repository coordination.
|
|
- Some current packages, especially artifact reading and defaults, must be
|
|
separated by responsibility rather than moved intact.
|
|
- Scriptorium's current configuration and documentation references must be
|
|
split between application and framework owners.
|
|
- Maintainers must inventory and migrate downstream consumers explicitly; no
|
|
compatibility facade will hide incomplete migration.
|
|
- Until the split is implemented, the current repository structure and
|
|
contracts remain authoritative.
|