# Step 7 Implementation Plan ## Status Accepted for implementation. Implement each stage in order. Do not begin a later stage until the current stage's exit criteria pass. The [Step 7 feature roadmap](step7.md) is the north star for scope, policy, desired behavior, and completion. [ADR 0002](../adr/0002-split-promptkit-from-scriptorium.md) controls the durable project boundary. This plan changes Scriptorium only. Promptkit `v0.1.0` is a published, read-only input to the cutover. Do not edit Promptkit, create a new Promptkit tag, or use the sibling Promptkit checkout to make an intermediate Scriptorium state pass. ## Implementation Rules Apply these rules throughout every stage: - Read both repositories' development, architecture, documentation, and testing policies before implementation. - Preserve unrelated working-tree changes. The removals of completed roadmap files that predate this plan are user-owned changes and must not be restored. - Run Scriptorium commands outside a Go workspace. Do not commit `go.work`, `go.work.sum`, a local `replace`, vendored Promptkit source, or generated dependency output. - Depend only on `gitea.maximumdirect.net/eric/promptkit v0.1.0` and its root `promptkit` package. Do not import `promptkit/internal/...`. - Treat the former Scriptorium Go package as intentionally removed. Do not add aliases, forwarding wrappers, deprecated shims, or an application-local copy of Promptkit values. - Preserve CLI, HTTP, configuration, formatting, artifact-containment, security, error-mapping, and process behavior unless the feature roadmap explicitly says otherwise. - Prefer adapting existing application tests to Promptkit values over adding parallel test suites. Delete tests whose only owner is the removed framework. - Keep permanent documentation aligned with implemented state. Do not describe a later stage as complete before its code has landed. - Use `gofmt` for Go formatting and `go mod tidy` for module metadata. Do not hand-edit `go.sum`. If a required capability is absent from Promptkit `v0.1.0`, stop the Step 7 implementation and report the exact public-boundary gap. Do not work around it with a Scriptorium framework copy or an unpublished Promptkit revision. ## Stage 1: Cut The Application Seam Over To Promptkit ### Objective Make all retained Scriptorium application code and tests consume the published Promptkit facade while the old local framework remains temporarily available for comparison. This stage proves that `v0.1.0` is sufficient before deleting the old implementation. ### Module Dependency Add an exact direct requirement: ```text gitea.maximumdirect.net/eric/promptkit v0.1.0 ``` Resolve it from the configured module source with `GOWORK=off`. Do not add a replacement. Do not prune the old framework-only dependencies yet; the local framework still compiles during this stage. Confirm before changing imports that the selected module resolves to `v0.1.0` and that its origin hash is the accepted Promptkit release commit `9e68a2bbf779545995270c47842048a3bc6c85dc`. ### Production Import And Type Migration Replace the former root-package import with `gitea.maximumdirect.net/eric/promptkit` in every retained application package. Use the package name `promptkit` in code rather than aliasing it back to `scriptorium`. Update `internal/adapter/cli/run.go` so: - engine construction calls `promptkit.NewEngine` with `promptkit.Config`; - `run` and `render` requests use `promptkit.RunRequest`; - input mappings use `promptkit.File` and Promptkit artifact references; - execution overrides use `promptkit.ExecutionTargetOverride`; - the `serve` path injects the HTTP reader with `promptkit.WithArtifactReader`; - result summaries, validation classification, and exit-code selection use Promptkit result and status values; and - no CLI flag, precedence, output, or error wording changes solely because of the import migration. Update `internal/adapter/http` so: - the adapter-owned `Runner` interface accepts `promptkit.RunRequest` and returns `*promptkit.RunResult`; - DTO mapping constructs Promptkit artifact references and execution overrides; - response mapping reads Promptkit artifact, validation, model, usage, and metadata values directly; - HTTP error mapping uses Promptkit's public sentinel errors with `errors.Is`; and - the restricted reader implements `promptkit.ArtifactReader` and uses Promptkit artifact reference, artifact, and reference-type values. Keep `ErrFileNotAllowed`, `ErrFileOutsideRoot`, and `ErrFileTooLarge` in Scriptorium. Confirm that Promptkit's artifact-load wrapping preserves those underlying identities so the existing HTTP mappings continue to work. Update `internal/format` so every formatter interface, entry point, message collection, and test fixture uses Promptkit prepared-run and message values directly. ### Test Migration Update retained CLI, HTTP, restricted-reader, and formatter tests to import and construct Promptkit public values. In particular: - fake runners must use the Promptkit request/result signature; - handler integration helpers must construct a real `promptkit.Engine`; - test LLM clients must implement `promptkit.LLMClient`; - engine options must use Promptkit options; - error-mapping tables must use Promptkit sentinels; and - formatting fixtures must use Promptkit public prepared-run types. Do not change expected CLI output, HTTP payloads, status codes, error codes, artifact policy, or formatted render output merely to accommodate the new package name. ### Validation Run: ```sh go mod download gitea.maximumdirect.net/eric/promptkit@v0.1.0 go list -m -f '{{.Path}} {{.Version}}' gitea.maximumdirect.net/eric/promptkit go test ./internal/adapter/... ./internal/format/... go test ./... go vet ./... go build ./cmd/scriptorium ``` Also run the maintained render script and the existing CLI command tests that exercise built-in profiles, custom profiles, explicit zero-valued overrides, restricted HTTP artifacts, public error mapping, and prepared-run formatting. ### Exit Criteria - All retained application production code uses Promptkit public types. - Application tests use Promptkit at the same boundaries as production. - The exact direct `v0.1.0` dependency is selected without a replacement. - The full repository still builds and tests while the local framework copy is present but unused by retained application code. - No Promptkit API gap has been discovered. ## Stage 2: Remove The Duplicated Framework ### Objective Delete every Promptkit-owned implementation, facade, test, fixture, and example from Scriptorium, then reduce application defaults and module dependencies to the slim repository boundary. ### Remove The Former Root Package Delete the complete former root facade: - `artifact_reader.go` - `artifact_reader_internal_test.go` - `convert.go` - `engine.go` - `engine_test.go` - `errors.go` - `formatting.go` - `json_copy.go` - `llm_adapter.go` - `profiles.go` - `types.go` After deletion, the Scriptorium module root must contain no `.go` files and must not be importable as package `gitea.maximumdirect.net/eric/scriptorium`. Do not replace these files with a stub package or migration shim. ### Remove Promptkit-Owned Internal Packages Delete these directories in full, including their package tests, testdata, and embedded assets: - `internal/artifact` - `internal/domain` - `internal/filecatalog` - `internal/llm` - `internal/profile` - `internal/prompt` - `internal/promptdef` - `internal/usecase` - `internal/validate` Delete `testdata/framework`. Promptkit owns the framework contract corpus and already contains its maintained copy. Delete `examples/go-library`. The Scriptorium Go-library example is obsolete; Promptkit owns the maintained in-process preparation example. Do not delete the executable assets under `examples/config*.yml`, `examples/prompts`, `examples/profiles`, `examples/schemas`, `examples/fixtures`, `examples/http-run.json`, or `examples/render-markdown-summary.sh`. Those remain Scriptorium application examples even though Promptkit owns their file-format semantics. ### Split Defaults By Ownership Reduce `internal/defaults/defaults.go` to values still owned and used by Scriptorium: - `HTTPAddrDefault` - `SchemaDirDefault`, which remains the application configuration default for Scriptorium's schema source location; - `HTTPMaxRequestBytesDefault` - `HTTPMaxArtifactBytesDefault` - `HTTPMaxResponseBytesDefault`; and - `HTTPReadHeaderTimeoutDefault`. Remove framework-owned output-artifact names, content types, OpenAI-compatible paths, execution defaults, model-client timeouts, domain imports, and `ExecutionTargetDefault`. Do not copy constants from Promptkit merely to make tests convenient. When the CLI does not supply a framework setting, continue passing the zero value so Promptkit applies its own default. ### Prune Module Metadata Run `go mod tidy` only after all Promptkit-owned source and tests are gone. Keep `gopkg.in/yaml.v3` as a direct dependency because Scriptorium still decodes application configuration. Allow `go mod tidy` to remove or reclassify JSON Schema and other modules according to the actual remaining import graph. The final `go.mod` must have: - Promptkit `v0.1.0` as a direct dependency; - no `replace` directive; - no dependency retained solely for deleted framework code; and - the existing compatible Go language version. ### Validation Run: ```sh go mod tidy go list ./... go test ./... go vet ./... go build ./cmd/scriptorium ``` Inspect `go list ./...` and the filesystem. The package inventory must consist only of the command and retained application packages: adapters, configuration, defaults, and formatting. The module root and removed framework package families must be absent. ### Exit Criteria - Scriptorium exposes no root Go package. - No duplicated framework package, built-in registry, framework testdata, or Go-library example remains. - Application defaults contain no Promptkit-owned behavior. - Module metadata reflects the actual slim application graph. - All remaining packages test, vet, and build against Promptkit `v0.1.0`. ## Stage 3: Harden The Boundary And Rationalize Tests ### Objective Make the slim dependency direction durable and leave Scriptorium with a lean test suite that protects application behavior rather than retesting Promptkit. ### Architecture Guard Rewrite `internal/adapter/dependency_test.go` as a repository-level application-boundary guard. It may remain in the `internal/adapter` test package, but it must locate the module root reliably and recursively inspect all non-test production `.go` files under the repository. The scanner must: - skip `.git`, `vendor`, and other non-source output directories; - parse imports with the Go parser instead of searching raw source text; - reject the exact former root import `gitea.maximumdirect.net/eric/scriptorium`; - reject the former Scriptorium framework package families removed in Stage 2; - reject `gitea.maximumdirect.net/eric/promptkit/internal` and every path below it; and - continue allowing retained Scriptorium application-internal imports such as `internal/config`, `internal/defaults`, `internal/format`, and adapter packages. Add focused scanner tests using temporary nested source trees to prove exact root-package detection, nested former-family detection, Promptkit-internal detection, and allowance of retained application packages. Add an architecture assertion that: - the module root has no production `.go` file; and - each removed framework directory is absent. This is an intentional architecture invariant, not a generic assertion that the whole repository tree can never change. ### Test Ownership Audit Review every remaining test after the mechanical import migration: - `internal/adapter/cli` owns CLI parsing, configuration/flag mapping, command output, summaries, exit codes, and representative real-engine command workflows; - `internal/adapter/http` owns DTO mapping, strict JSON, status/error mapping, limits, restricted artifact policy, cancellation, and representative real-engine handler workflows; - `internal/config` owns strict application YAML, discovery, precedence, validation, and application defaults; - `internal/format` owns deterministic text/JSON presentation; and - the architecture guard owns dependency direction and removal invariants. Retain the existing real Promptkit-engine adapter tests because they protect the consumer integration seam. Keep their prompt and profile fixtures package-local or generated with `t.TempDir`; do not recreate `testdata/framework`. Delete or simplify any assertion that only duplicates Promptkit parser, orchestration, profile, validation, model-client, or public-contract tests. Do not add a Scriptorium table for every Promptkit sentinel or field. Preserve only mappings Scriptorium exposes through CLI or HTTP behavior. Add a new test only when the cutover exposes an application-owned risk that is not already covered. Prefer updating an existing adapter integration test over creating a new end-to-end harness. ### Release Workflow Check Inspect `.woodpecker/release.yml` and any packaging metadata for assumptions about the removed root package or framework directories. Keep the release target at `./cmd/scriptorium` (or its canonical module package path) and ensure the workflow obtains Promptkit through ordinary module resolution. Do not add a Promptkit checkout, workspace, replacement, vendor step, or cross-repository artifact to Scriptorium CI or release packaging. ### Validation Run: ```sh go test ./... go test -race ./... go vet ./... go build ./cmd/scriptorium ``` Run the architecture guard directly by name as a focused diagnostic. Confirm that its negative fixtures fail the scanner for the intended import and that the real repository produces no violation. Exercise the release workflow's Go build commands locally with temporary outputs for its supported target platforms. Do not create `dist/` or another tracked output directory during validation. ### Exit Criteria - The repository guard protects the complete new dependency direction. - Remaining tests have an explicit application-owned purpose. - No deleted Promptkit behavior is redundantly reimplemented in tests or fixtures. - Race-enabled tests, vet, application builds, and release-equivalent builds pass. ## Stage 4: Reconcile Permanent Documentation And Examples ### Objective Make all permanent Scriptorium documentation describe the implemented slim application and route framework readers to the version of Promptkit the application actually consumes. ### Version-Appropriate Promptkit Links Use the tagged Promptkit `v0.1.0` documents as the canonical framework references during this step. Link to the corresponding files under: ```text https://gitea.maximumdirect.net/eric/promptkit/src/tag/v0.1.0/ ``` Use these owners where applicable: - `docs/consumers/pkg-promptkit.md` for Go-library consumer guidance; - `docs/formats.md` for prompt, profile, schema, execution-setting, built-in profile, and framework credential contracts; and - `docs/integrations/openai-compatible-chat.md` for outbound provider wire and timeout behavior. Do not copy their field tables, defaults, error inventories, or timeout rules into Scriptorium. Scriptorium documents only its own mapping or invocation surface and links to the Promptkit owner for the framework definition. ### Orientation And Policy Update: - `README.md` to present only the runnable CLI and HTTP application, remove the Scriptorium Go-package link and Go-library example, and identify Promptkit as the imported framework; - `docs/policy/architecture.md` to define the current application-only shape, retained package boundaries, Promptkit dependency direction, HTTP artifact security boundary, application invariants, and non-goals; - `docs/development.md` to route contributors to the remaining adapter, configuration, formatting, packaging, and Promptkit-consumer boundaries; - `docs/policy/documentation.md` where its ownership table or boundary rules still imply that Scriptorium owns a public Go package or framework integration; and - `docs/policy/testing.md` where framework-domain, validator, model-client, or orchestration examples imply those are still Scriptorium test owners. Do not rewrite accepted ADR decision content. ADR 0002 remains the permanent decision record even where its context describes the pre-split state. ### External And User Contracts Audit and update: - `docs/cli.md` - `docs/api.md` - `docs/config.md` - `docs/operations.md` - `docs/consumers/api.md` - `docs/integrations/subprocess.md` Preserve Scriptorium-owned command syntax, configuration discovery and server fields, HTTP schemas and mappings, operations, and subprocess behavior. Remove claims that Scriptorium provides an in-process Go API. Where these documents name prompt/profile fields, model semantics, built-in profiles, framework credentials, or timeout behavior, keep only the Scriptorium mapping context and link to the tagged Promptkit owner. Delete `docs/consumers/pkg-scriptorium.md`. Update every incoming link so Go framework consumers are directed to Promptkit rather than to a compatibility document. Delete `docs/integrations/openai-compatible-chat.md` after replacing all incoming Scriptorium links with the tagged Promptkit integration contract. Scriptorium no longer owns the outbound provider protocol. ### Internal Documentation Update `docs/internal/overview.md` to inventory exactly: - `cmd/scriptorium`; - `internal/adapter/cli`; - `internal/adapter/http`; - `internal/config`; - `internal/defaults`; and - `internal/format`. Update `docs/internal/adapters.md` for Promptkit request/result mapping, engine construction, consumer-owned interfaces, error mapping, output behavior, and restricted-reader injection. Rewrite `docs/internal/sources.md` around Scriptorium-owned application source locations and the restricted HTTP artifact reader. Link Promptkit for ordinary artifact loading and framework source semantics. Delete: - `docs/internal/runner.md` - `docs/internal/llm.md` Promptkit owns those subsystem documents. Remove all incoming links rather than leaving placeholder internal documents. ### Examples And Navigation Keep the executable configuration, prompt, profile, schema, fixture, HTTP, and render-script examples. Confirm they still run against the tagged dependency and that prose describes them as Scriptorium application examples. Remove every link to `examples/go-library`, root Go declarations, removed framework directories, deleted Scriptorium consumer documentation, and deleted internal or integration documents. Search all permanent Markdown outside `docs/roadmap/` for: - the former root import path used as a Go package; - claims that Scriptorium owns or implements the framework; - references to removed packages, tests, fixtures, or examples; and - duplicate Promptkit framework contracts. The migration roadmap and accepted ADRs may retain historical and future language appropriate to their lifecycle. ### Validation Validate every local Markdown link and follow every new external Promptkit link. Run: ```sh git diff --check go test ./... go vet ./... go build ./cmd/scriptorium ``` Run the maintained render script and both `examples/config.yml` and `examples/config.full.yml` through a credential-free `render` workflow. No documentation validation may invoke a paid or live model endpoint. ### Exit Criteria - Permanent docs describe only the implemented application boundary. - Framework contracts have one Promptkit owner and version-appropriate links. - Removed code and examples have no live incoming links. - All retained examples are valid, secret-free, and runnable offline where intended. - Documentation links, whitespace, tests, vet, and the executable build pass. ## Stage 5: Perform Independent Acceptance And Record Completion ### Objective Validate the exact final state without sibling-repository coupling, then record the Step 7 gate only after every feature-roadmap completion criterion is satisfied. ### Scriptorium Acceptance From the Scriptorium repository root, confirm no Go workspace is active and no replacement or vendored dependency exists. Validate module identity and selection: ```sh go list -m -f '{{.Path}} {{.GoVersion}}' go list -m -f '{{.Path}} {{.Version}}' gitea.maximumdirect.net/eric/promptkit go mod graph go mod tidy -diff ``` Require the Scriptorium module path and existing Go version, and require Promptkit to resolve exactly to `v0.1.0`. Inspect the graph for a normal tagged module edge rather than a local path. Run: ```sh go test ./... go test -race ./... go vet ./... go build ./cmd/scriptorium gofmt -l $(git ls-files '*.go') git diff --check ``` The formatting command must produce no paths. Build the executable to a temporary directory rather than the repository. Reproduce the release workflow's supported cross-compilation targets and linker flags with temporary outputs. Run the maintained render script and credential-free render commands using both configuration examples. Verify representative HTTP behavior through the retained handler tests, including strict decoding, request/response size limits, restricted file access, public error mapping, and optional raw output. Validate all maintained Markdown links, including the tagged Promptkit links. Search the final tracked tree for former root-facade imports, removed framework packages, built-in asset copies, framework fixtures, Go-library examples, workspaces, replacements, generated binaries, credentials, and stale documentation references. ### Clean Remote-Dependency Verification Use a fresh temporary module cache with `GOWORK=off` and ordinary remote module resolution to download dependencies and run at least the Scriptorium build and test suite. The verification must not read the sibling Promptkit working tree. Confirm the downloaded Promptkit module reports version `v0.1.0` and origin commit `9e68a2bbf779545995270c47842048a3bc6c85dc`. Do not clear or destructively rewrite the maintainer's normal module cache. Use a temporary directory and remove it after validation. ### Independent Promptkit Reconfirmation Without changing Promptkit, validate the exact `v0.1.0` source from a temporary detached worktree or the downloaded module directory. If the clean Promptkit checkout still has `HEAD` exactly at the peeled `v0.1.0` commit, it may be used directly. Run Promptkit's documented sequence from that exact source: ```sh go test ./... go test -race ./... go vet ./... go build ./... go run ./examples/go-library/prepare ``` Also check Promptkit formatting, links, whitespace, module tidiness, absence of a workspace/replacement, clean status, and the local and remote `v0.1.0` tag target. Remove any temporary detached worktree after validation. This proves that Scriptorium's adoption did not introduce cross-repository state or require an unpublished Promptkit change. ### Completion Records Only after all acceptance checks pass: - update `docs/roadmap/step7.md` status from Proposed to Complete and replace future-oriented starting-state material as needed with a concise result, retained application boundary, validation evidence, and Step 8 handoff; - update `docs/roadmap/migration.md` so its status says Steps 1 through 7 are complete and its Step 7 gate records the tagged dependency, removal of the framework copy and public facade, independent validation, and readiness for downstream migration; and - replace this staged checklist with a concise Step 7 implementation completion record containing the dependency version, resulting package boundary, removed ownership, validation evidence, and next gate. Do not mark Step 8 or Step 9 complete. Do not publish a Scriptorium release or claim that external consumers have migrated. ### Exit Criteria - Every completion criterion in `docs/roadmap/step7.md` is demonstrably true. - Scriptorium passes release-grade validation against remotely resolved Promptkit `v0.1.0`. - Promptkit remains independently clean and valid at its published tag. - Permanent documentation, examples, module metadata, and architecture checks match the final slim application. - Completion records accurately authorize Step 8 without claiming later work.