Files
scriptorium/docs/adr/0002-split-promptkit-from-scriptorium.md

14 KiB

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:

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.