17 KiB
Feature 0004: Scriptorium Source Installation and Configuration
Status: done
Summary
Provide a small, independently usable rakestraw.scriptorium role that
installs Scriptorium from a pinned Git tag, builds its executable on the
managed host, and manages its native YAML configuration. Preserve
Scriptorium's usefulness inside and outside the D&D transcript pipeline by
keeping application configuration in the standalone role and leaving prompt
library content and suite policy to their eventual owners.
Problem or Opportunity
The ansible-role-scriptorium repository contains only its initial README and
license. Scriptorium is one of the five applications required by the pipeline,
but it is also a generally useful Promptkit-based application with independent
consumers. It needs a complete standalone installation and configuration role
before the pipeline role can compose it without owning Scriptorium-specific
behavior.
The completed Seriatim, Audita, and Notarius roles establish the concise source-build pattern. Scriptorium can reuse that pattern while exposing its own configuration contract through field-specific, nullable variables.
Governing Policies
- The Ansible house style governs the role implementation, public configuration interface, and template.
- The testing house style governs repository-local validation. Tests exercise role-owned behavior through public inputs and native application outcomes without snapshotting defaults, tracing tasks, or rechecking trusted-module postconditions.
- The MVP roadmap fixes the common source-build architecture, supported platform, filesystem layout, and standalone-role boundary.
Scope
- Implement
ansible-role-scriptoriumas a conventional standalone role. - Support Ubuntu 26.04 on native
amd64andarm64hosts. - Install Git and the official Go Snap with classic confinement when build dependency management is enabled.
- Default the Go Snap channel to
1.26/stable. - Check out immutable Scriptorium tag
v0.13.0by default under/usr/local/src/scriptorium. - Build the native executable with Scriptorium's release build contract and
install it at
/usr/local/bin/scriptorium. - Rebuild when the Git checkout changes or the binary is absent.
- Converge the source tree to
root:rootownership and the installed binary toroot:rootownership and mode0755. - Manage
/usr/local/etc/scriptorium/config.ymlasroot:rootwith mode0644using one role-owned template. - Render
{}when no application settings are selected, allowing Scriptorium to use its built-in defaults without copying them into Ansible. - Expose every application configuration field through a field-specific role variable. Default each field to null and omit it from the rendered document until a caller supplies a non-null value.
- Expose the native open-ended
backendsmapping as one nullable variable and serialize the caller's mapping without inventing a second backend schema or merge mechanism. - Keep defaults, argument specifications, README, metadata, collection dependency declaration, template, and focused integration tests aligned with this contract.
Non-goals
- D&D-specific prompts, profiles, schemas, model choices, paths, or other pipeline policy.
- Cloning, copying, or otherwise populating prompt, profile, or schema libraries.
- A role-managed Scriptorium service, HTTP listener lifecycle, scheduler, or persistent runtime account.
- A raw mapping for the complete configuration document, a template override, or parallel configuration interfaces.
- Copying Scriptorium's documented built-in defaults into role defaults merely to render them back into YAML.
- Decomposing the open-ended
backendsmapping into variables for each backend ID or backend-specific option. - Secret values, environment files, or another credential-delivery mechanism.
- Installation or configuration of Promptkit, another pipeline application, or the complete D&D suite.
- Installation from Scriptorium's published binary artifacts, cross-compiling, or supporting platforms outside the MVP.
- Production execution of Scriptorium to validate its binary or configuration.
- Detection of out-of-band binary modification, compiler changes, or fixed build-command changes.
- Build fingerprints, checksums, sidecar state, staged replacement, manual Git cleanup, or special recovery for malformed or adversarial host state.
Target End State
On a supported host, applying the role with defaults installs or confirms Git
and Go, checks out Scriptorium v0.13.0 at
/usr/local/src/scriptorium, builds /usr/local/bin/scriptorium, and manages
/usr/local/etc/scriptorium/config.yml. The managed configuration is the empty
mapping {}, so Scriptorium retains ownership of all application defaults.
When a caller sets an optional configuration variable, the corresponding
native YAML field appears in the correct section. Null fields are absent, as
are parent mappings with no selected descendants. Explicit false, zero, empty
string, empty collection, and other non-null values are serialized rather than
silently omitted, leaving their native semantics to Scriptorium.
scriptorium_backends represents the complete native backends field as a
mapping keyed by backend ID; the role does not rewrite its nested definitions
or combine fragments. Callers and composing roles may construct the final
mapping with ordinary inventory and Jinja expressions.
Prompt, profile, and schema directories remain caller-provided application inputs. The standalone role exposes their configuration paths but does not populate them. A later pipeline feature will deploy the maintained prompt library and pass its selected locations through this public interface.
An immediate repeated application reports no changes. A changed checkout or a missing binary causes the build command to run. Configuration changes converge through the template without becoming build triggers.
Repository Ownership
ansible-role-scriptoriumowns the role implementation, public variables, native configuration serialization, metadata, dependency declaration, documentation, and repository-local tests.../scriptoriumis the read-only authority for release, build, CLI, and configuration contracts.rakestraw-ansibleis a read-only style and historical deployment reference.ansible-role-dnd-pipelinewill later own D&D-specific Scriptorium values and prompt-library deployment through the standalone role's public interface.ansible-workspace-dndowns this roadmap, the eventual staged implementation plan, and aggregate status.- All other repositories remain unchanged.
Constraints and Settled Decisions
- The selected default release is annotated tag
v0.13.0, resolving to commitf33af205242cbaeeb689c179c0465bd10beaa1a3. - The release declares Go 1.25.5. The suite-standard
1.26/stableGo Snap channel satisfies that requirement. - The native build uses package
./cmd/scriptorium,CGO_ENABLED=0,GOWORK=off,GOTOOLCHAIN=local,-trimpath, and linker flags-s -w. - Scriptorium does not embed the selected application tag and does not expose a native application-version command. Production convergence does not add version interrogation, and tests do not inspect the Git checkout merely to manufacture version evidence.
- Scriptorium accepts an empty configuration and resolves all omitted settings
from built-in defaults. The role renders
{}so the managed file is an explicit YAML mapping while selecting no application policy. - Configuration discovery checks
/usr/local/etc/scriptorium/config.ymlbefore/etc/scriptorium/config.yml; the role manages the first path. - Optional variables use YAML null defaults and template conditions use
is not none. Parent mappings are emitted only when at least one descendant is non-null. - Strings and mappings are serialized with Ansible or Jinja filters rather than hand-written YAML quoting.
scriptorium_backendsis a native open-ended mapping. Caller-side mapping composition is permitted, but the role supplies no merge or precedence behavior.- Backend configuration may name a credential environment variable through
api_key_env; secret values themselves remain outside the role and managed file. - Tasks installing dependencies or managing root-owned state use task-level
become: true. community.generalsupplies Snap management and is pinned to version13.3.0in the role's collection requirements.
Fixed Public Interface
Provide these installation variables:
| Variable | Default | Purpose |
|---|---|---|
scriptorium_version |
v0.13.0 |
Selected Git revision |
scriptorium_repository_url |
https://gitea.maximumdirect.net/eric/scriptorium.git |
Source repository |
scriptorium_source_dir |
/usr/local/src/scriptorium |
Managed source checkout |
scriptorium_binary_path |
/usr/local/bin/scriptorium |
Installed executable |
scriptorium_manage_build_dependencies |
true |
Whether to install Git and Go |
scriptorium_go_snap_channel |
1.26/stable |
Go Snap channel |
scriptorium_go_binary |
/snap/bin/go |
Go executable used for the build |
scriptorium_config_path |
/usr/local/etc/scriptorium/config.yml |
Managed configuration file |
Provide these nullable configuration variables:
| Variable | Native field |
|---|---|
scriptorium_prompt_dir |
prompt_dir |
scriptorium_profile_dir |
profile_dir |
scriptorium_schema_dir |
schema_dir |
scriptorium_server_addr |
server.addr |
scriptorium_server_artifact_root |
server.artifact_root |
scriptorium_server_max_request_bytes |
server.max_request_bytes |
scriptorium_server_max_artifact_bytes |
server.max_artifact_bytes |
scriptorium_server_max_response_bytes |
server.max_response_bytes |
scriptorium_defaults_render_format |
defaults.render_format |
scriptorium_backends |
backends |
Argument specifications type paths and strings according to their native
shape, the three server limits as integers, and scriptorium_backends as a
mapping. They do not reproduce semantic validation owned by Scriptorium. The
role preserves non-null values while the application decides whether a
selected value is valid or effective.
Simplest Viable Implementation
The expected production implementation is approximately nine ordinary tasks in one readable task file:
- Assert Ubuntu 26.04 and architecture
x86_64oraarch64. - Install Git with
ansible.builtin.apt, includingcache_valid_time: 3600, when dependency management is enabled. - Install the classic Go Snap from
scriptorium_go_snap_channelwithcommunity.general.snapwhen dependency management is enabled. - Create the source, binary, and configuration parent directories with one
ansible.builtin.fileloop. - Check out
scriptorium_versionwithansible.builtin.git,force: true, and register the module result. - Inspect whether
scriptorium_binary_pathexists withansible.builtin.stat. - Run the native Go build through
scriptorium_go_binarywhen the checkout changed or the binary is absent. - Converge recursive source ownership and binary ownership and mode with one
item-driven
ansible.builtin.fileloop. - Render the field-specific configuration with one
ansible.builtin.templatetask.
Use argv for the build command, declare its change semantics, and put its two
conditions directly on the task. Do not add a rebuild fact, handler, executable
discovery, compiler or application interrogation, configuration assembly facts,
preliminary configuration probes, or post-operation validation to production
tasks.
Rationale and Alternatives
A standalone role is valuable beyond the D&D pipeline and prevents the suite role from becoming the owner of Scriptorium's native application interface. The suite can later provide opinionated directory paths and backend choices without reducing the role's usefulness to independent consumers.
Field-specific nullable variables preserve a documented and typed inventory
interface while allowing upstream defaults to evolve. Rendering {} is
preferable to copying those defaults into Ansible and is more explicit than an
empty file. Conditional sections distinguish omission from intentional falsey
values.
The backends mapping is the deliberate exception to leaf-level variables.
Its keys are caller-defined backend IDs and its values follow an extensible
native schema. Passing that one native field through as a mapping is simpler
and more useful than inventing variables for arbitrary IDs, while still
avoiding a raw mapping for the complete configuration document.
The role exposes server configuration because it is part of Scriptorium's native interface, but it does not infer that callers want a managed service. Consumers may invoke the CLI directly or arrange their own process lifecycle.
Scriptorium now publishes binary release artifacts, but the MVP deliberately uses the common source-build model across all five applications. Artifact-based installation remains a later cross-suite decision.
Acceptance Criteria
Required Role Behavior
- The role rejects unsupported platforms before modifying application state.
- On a supported host, it installs the selected Scriptorium source release and builds the binary with the upstream release contract.
- Source, binary, and configuration state converge to the paths, ownership, and modes selected above through the responsible Ansible modules.
- With no configuration variables selected, the template renders
{}and Scriptorium accepts the managed file while retaining its built-in defaults. - Each non-null configuration variable renders its corresponding native field. Null fields and empty parent sections are omitted, while intentional falsey values are retained.
- A caller-supplied backend mapping is rendered without structural rewriting, and independently defined mappings can be composed before assignment.
- A changed checkout or missing binary causes the imperative build to run. An immediate repeated application with unchanged inputs reports no changes.
- Dependency opt-out accepts a caller-supplied
scriptorium_go_binary. - Root-owned operations use task-level privilege escalation; play-level escalation is not required.
- Defaults, argument specifications, template behavior, README, and metadata remain aligned with the public interface.
- Production tasks contain no role-private state, redundant probes, application self-tests, or speculative guarantees.
- No repository other than
ansible-role-scriptoriumand the workspace control repository is modified.
Required Validation
- Syntax checking and production-profile ansible-lint pass.
- A default invocation exercises the installed application with the managed configuration, followed immediately by a role application reporting no changes. It does not snapshot the role's current default tag or complete default document.
- One recovery case removes the binary, disables dependency management, supplies a caller-selected Go executable, confirms that executable is used, and finishes with a clean repeated application.
- One representative configuration case verifies selected scalar and nested values, selected omissions, intentional zero values, and a caller-supplied backend mapping. An offline native Scriptorium command accepts and uses the managed configuration, and an immediate repeated application reports no changes.
- Tests do not assert task names or order, individual task results, exact current defaults, complete configuration snapshots, public-variable counts, trusted-module file or package postconditions, or an exhaustive negative platform matrix.
- Because Scriptorium exposes no native application release identity, the test suite does not substitute a Git-checkout assertion or version-specific behavior snapshot for a native version-selection test.
Dependencies and Follow-up Work
The managed host requires access to the Scriptorium Git repository, Ubuntu Snap infrastructure, and Go module sources. Useful prompt execution also needs prompt definitions and, depending on the chosen configuration, profiles, schemas, backend endpoints, and credential environment variables. Those inputs remain caller responsibilities for this standalone feature.
With this feature complete, the Narratio standalone role can be designed against all four downstream application-role interfaces. The later pipeline role will install maintained prompt-library content and supply D&D-specific Scriptorium configuration through this role.
The role was implemented in ansible-role-scriptorium commits 90d7f45 and
5944eac, then finalized after independent review by remediation commit
8f47aa8. Its complete integration suite passes.
Potential Future Hardening
- Build into a temporary path before replacing the installed executable.
- Verify source signatures or release artifacts.
- Detect compiler, build-command, or out-of-band binary changes.
- Validate configuration during production convergence.
- Manage a service, runtime account, secret environment, or network controls if later operating requirements select them.
- Support Scriptorium's published binary artifacts as part of a future suite-wide installation-model change.
- Add broader platform coverage only when supported-platform behavior expands.