2026-09-09 14:20:26 +00:00
|
|
|
# Feature 0004: Scriptorium Source Installation and Configuration
|
|
|
|
|
|
2026-09-09 19:17:08 +00:00
|
|
|
Status: done
|
2026-09-09 14:20:26 +00:00
|
|
|
|
|
|
|
|
## 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](../../ansible.md) governs the role implementation,
|
|
|
|
|
public configuration interface, and template.
|
|
|
|
|
- [The testing house style](../../testing.md) 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](../mvp.md) fixes the common source-build architecture,
|
|
|
|
|
supported platform, filesystem layout, and standalone-role boundary.
|
|
|
|
|
|
|
|
|
|
## Scope
|
|
|
|
|
|
|
|
|
|
- Implement `ansible-role-scriptorium` as a conventional standalone role.
|
|
|
|
|
- Support Ubuntu 26.04 on native `amd64` and `arm64` hosts.
|
|
|
|
|
- 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.0` by 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:root` ownership and the installed binary to
|
|
|
|
|
`root:root` ownership and mode `0755`.
|
|
|
|
|
- Manage `/usr/local/etc/scriptorium/config.yml` as `root:root` with mode
|
|
|
|
|
`0644` using 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 `backends` mapping 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 `backends` mapping 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-scriptorium` owns the role implementation, public variables,
|
|
|
|
|
native configuration serialization, metadata, dependency declaration,
|
|
|
|
|
documentation, and repository-local tests.
|
|
|
|
|
- `../scriptorium` is the read-only authority for release, build, CLI, and
|
|
|
|
|
configuration contracts.
|
|
|
|
|
- `rakestraw-ansible` is a read-only style and historical deployment reference.
|
|
|
|
|
- `ansible-role-dnd-pipeline` will later own D&D-specific Scriptorium values and
|
|
|
|
|
prompt-library deployment through the standalone role's public interface.
|
|
|
|
|
- `ansible-workspace-dnd` owns 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 commit
|
|
|
|
|
`f33af205242cbaeeb689c179c0465bd10beaa1a3`.
|
|
|
|
|
- The release declares Go 1.25.5. The suite-standard `1.26/stable` Go 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.yml`
|
|
|
|
|
before `/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_backends` is 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.general` supplies Snap management and is pinned to version
|
|
|
|
|
`13.3.0` in 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:
|
|
|
|
|
|
|
|
|
|
1. Assert Ubuntu 26.04 and architecture `x86_64` or `aarch64`.
|
|
|
|
|
2. Install Git with `ansible.builtin.apt`, including
|
|
|
|
|
`cache_valid_time: 3600`, when dependency management is enabled.
|
|
|
|
|
3. Install the classic Go Snap from `scriptorium_go_snap_channel` with
|
|
|
|
|
`community.general.snap` when dependency management is enabled.
|
|
|
|
|
4. Create the source, binary, and configuration parent directories with one
|
|
|
|
|
`ansible.builtin.file` loop.
|
|
|
|
|
5. Check out `scriptorium_version` with `ansible.builtin.git`, `force: true`,
|
|
|
|
|
and register the module result.
|
|
|
|
|
6. Inspect whether `scriptorium_binary_path` exists with
|
|
|
|
|
`ansible.builtin.stat`.
|
|
|
|
|
7. Run the native Go build through `scriptorium_go_binary` when the checkout
|
|
|
|
|
changed or the binary is absent.
|
|
|
|
|
8. Converge recursive source ownership and binary ownership and mode with one
|
|
|
|
|
item-driven `ansible.builtin.file` loop.
|
|
|
|
|
9. Render the field-specific configuration with one
|
|
|
|
|
`ansible.builtin.template` task.
|
|
|
|
|
|
|
|
|
|
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-scriptorium` and 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.
|
|
|
|
|
|
2026-09-09 19:17:08 +00:00
|
|
|
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.
|
2026-09-09 14:20:26 +00:00
|
|
|
|
|
|
|
|
## 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.
|