Plan the Scriptorium role implementation
This commit is contained in:
@@ -0,0 +1,331 @@
|
||||
# Feature 0004: Scriptorium Source Installation and Configuration
|
||||
|
||||
Status: ready
|
||||
|
||||
## 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.
|
||||
|
||||
After this feature, 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.
|
||||
|
||||
## 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.
|
||||
@@ -1,11 +1,377 @@
|
||||
# Implementation Plan
|
||||
# Implementation Plan 0004: Install and Configure Scriptorium
|
||||
|
||||
Status: pending
|
||||
Status: ready
|
||||
|
||||
No implementation plan is currently active. Test-suite remediation R0001 was
|
||||
implemented in role commits `e08c63c`, `d2d0b9d`, and `cfecf37`, then reviewed
|
||||
successfully. Its rationale and validation record remain in
|
||||
[the test-suite audit findings](test-suite-audit-findings.md).
|
||||
Target executor: GPT-5.6-Terra
|
||||
|
||||
Select and approve the next unit of work before replacing this notice with a
|
||||
new staged implementation plan.
|
||||
Feature roadmap:
|
||||
[Feature 0004: Scriptorium Source Installation and Configuration](features/0004-scriptorium-source-installation-and-configuration.md)
|
||||
|
||||
Active plan location: `docs/roadmap/implementation.md`
|
||||
|
||||
## Objective
|
||||
|
||||
Implement the small, independently usable `rakestraw.scriptorium` role defined
|
||||
by Feature 0004. Install Scriptorium from its pinned source release, build its
|
||||
executable on the managed host, and manage its YAML configuration through the
|
||||
approved nullable field-specific interface without adding D&D-specific
|
||||
content or service management.
|
||||
|
||||
The feature roadmap remains authoritative for scope, rationale, target end
|
||||
state, public variables, and acceptance criteria.
|
||||
|
||||
## Execution Rules
|
||||
|
||||
- Execute Stage 1 and Stage 2 in numeric order.
|
||||
- Immediately before each stage, read every document in that stage's Required
|
||||
Policy Review, then confirm its preconditions and the owning repository's
|
||||
working-tree state.
|
||||
- Modify only `ansible-role-scriptorium`. Treat this workspace,
|
||||
`../scriptorium`, `rakestraw-ansible`, and every other repository as
|
||||
read-only during out-of-band implementation.
|
||||
- Preserve unrelated changes. Stop if the role repository is unexpectedly
|
||||
dirty at a stage boundary.
|
||||
- Implement the feature roadmap's nine-operation module-driven flow. Trust
|
||||
APT, Snap, `file`, `git`, and `template` to inspect and converge the state
|
||||
they own.
|
||||
- Inspect only whether the binary exists because that result is independently
|
||||
required to decide whether the imperative build command must run.
|
||||
- Put checkout change and binary absence directly on the build task. Do not
|
||||
introduce another fact, handler, fingerprint, checksum, manifest, or
|
||||
role-private state.
|
||||
- Do not execute Scriptorium from production role tasks. Native verification
|
||||
belongs only in the integration suite.
|
||||
- Render configuration with one template task. Use `is not none`, omit empty
|
||||
parent sections, preserve falsey non-null values, and serialize values with
|
||||
filters rather than manual YAML quoting.
|
||||
- Accept `scriptorium_backends` as one caller-supplied native mapping. Do not
|
||||
define its arbitrary backend IDs as role variables or implement role-side
|
||||
file loading, merging, or precedence.
|
||||
- Exercise the role through public variables and native application outcomes.
|
||||
Do not assert current default values, task traces, or state already
|
||||
guaranteed by trusted modules.
|
||||
- Use fully qualified collection names and `argv` for command execution.
|
||||
- Run each stage's validation before committing it. Do not begin Stage 2 if
|
||||
Stage 1 fails validation.
|
||||
- Commit each completed stage with its specified plain-English message. Do not
|
||||
push.
|
||||
- Treat `/usr/local/src/scriptorium`, `/usr/local/bin/scriptorium`, and
|
||||
`/usr/local/etc/scriptorium/config.yml` as disposable role-managed test
|
||||
state. Restore the default role deployment through test cleanup.
|
||||
|
||||
## Starting State and Preconditions
|
||||
|
||||
- `ansible-role-scriptorium` is clean on `main` at
|
||||
`2cd69a4b2ead23b44f1aab51fd1b1f14ac326af2`, tracking `origin/main` without
|
||||
divergence.
|
||||
- This plan and the ready Feature 0004 roadmap are committed before
|
||||
out-of-band execution begins.
|
||||
- The Scriptorium managed source, binary, and configuration paths are absent
|
||||
before initial execution and contain no user data that must be preserved.
|
||||
- The local controller and managed-host proxy are Ubuntu 26.04 `x86_64`, with
|
||||
gathered facts and passwordless privilege escalation available.
|
||||
- Workspace tooling is available in `../.venv/bin` from the role repository:
|
||||
Ansible Core 2.21.3 and ansible-lint 26.8.0.
|
||||
- `community.general` 13.3.0 and Go Snap channel `1.26/stable` are available.
|
||||
- Upstream annotated tag `v0.13.0` resolves to
|
||||
`f33af205242cbaeeb689c179c0465bd10beaa1a3` and declares Go 1.25.5.
|
||||
- The native build uses package `./cmd/scriptorium`, `CGO_ENABLED=0`,
|
||||
`GOWORK=off`, `GOTOOLCHAIN=local`, `-trimpath`, and linker flags `-s -w`.
|
||||
- Scriptorium accepts an empty YAML mapping as application configuration and
|
||||
supplies built-in defaults for every omitted field.
|
||||
- `scriptorium inspect profile` is an offline native command that can exercise
|
||||
configuration loading with a test-owned endpoint profile without making a
|
||||
model request.
|
||||
- Scriptorium does not expose native application release identity. The plan
|
||||
deliberately omits a version-selection test rather than inspecting the Git
|
||||
checkout or snapshotting release-specific behavior.
|
||||
|
||||
## Repository Scope
|
||||
|
||||
Repository that changes:
|
||||
|
||||
- `ansible-role-scriptorium`: role implementation, public interface,
|
||||
configuration template, metadata, dependency declaration, README, and
|
||||
repository-local integration suite.
|
||||
|
||||
Repositories that may be inspected but must remain unchanged:
|
||||
|
||||
- `ansible-workspace-dnd`: authoritative roadmap, plan, policies, and status.
|
||||
- `../scriptorium`: authoritative release, build, CLI, and configuration
|
||||
contracts.
|
||||
- `rakestraw-ansible`: historical style and deployment reference.
|
||||
- Every other repository in `workspace.yml`.
|
||||
|
||||
## Stage 1: Implement the Complete Minimal Role
|
||||
|
||||
### Objective
|
||||
|
||||
Create a complete standalone role that installs and configures Scriptorium
|
||||
with the fixed public interface, concise production task flow, minimal
|
||||
configuration template, documentation, and baseline idempotence coverage.
|
||||
|
||||
### Required Policy Review
|
||||
|
||||
Before beginning this stage, read:
|
||||
|
||||
- `AGENTS.md`
|
||||
- `docs/orientation.md`
|
||||
- `docs/conventions.md`
|
||||
- `docs/ansible.md`
|
||||
- `docs/testing.md`
|
||||
|
||||
### Preconditions
|
||||
|
||||
- The starting revisions and upstream contracts above remain unchanged.
|
||||
- `ansible-role-scriptorium` has no uncommitted changes.
|
||||
- The workspace planning commit containing this plan exists.
|
||||
- The three managed Scriptorium paths contain no data that must be preserved.
|
||||
|
||||
### Owning Repository
|
||||
|
||||
`ansible-role-scriptorium`
|
||||
|
||||
### Required Changes
|
||||
|
||||
1. Retain the existing license and add the conventional role structure:
|
||||
`defaults/main.yml`, `meta/main.yml`, `meta/argument_specs.yml`,
|
||||
`tasks/main.yml`, `templates/config.yml.j2`,
|
||||
`collections/requirements.yml`, `ansible.cfg`, and focused files under
|
||||
`tests/`.
|
||||
2. Declare role name `scriptorium`, namespace `rakestraw`, author Eric
|
||||
Rakestraw, license `CC0-1.0`, Ubuntu 26.04 support, Ansible Core 2.18 or
|
||||
newer, an empty dependency list, and the repository's canonical issue
|
||||
tracker. Pin `community.general` to `13.3.0`.
|
||||
3. Define the eight installation variables and ten nullable configuration
|
||||
variables in the Feature 0004 Fixed Public Interface. Mirror their public
|
||||
names and native shapes in `meta/argument_specs.yml`:
|
||||
- use `path` for source, binary, Go executable, configuration, and the three
|
||||
application directory variables;
|
||||
- use `bool` for dependency management;
|
||||
- use `int` for the three server byte limits;
|
||||
- use `dict` for `scriptorium_backends`; and
|
||||
- use `str` for the remaining variables.
|
||||
Concrete installation defaults are required by the argument specification;
|
||||
nullable application settings are optional. Do not reproduce Scriptorium's
|
||||
semantic validation rules.
|
||||
4. Implement one readable `tasks/main.yml` with this flow:
|
||||
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 as
|
||||
`root:root` mode `0755` with one `ansible.builtin.file` loop;
|
||||
5. check out `scriptorium_version` with `ansible.builtin.git`,
|
||||
`force: true`, and register its module result;
|
||||
6. inspect only `scriptorium_binary_path` with `ansible.builtin.stat`;
|
||||
7. build directly to `scriptorium_binary_path` when the checkout changed or
|
||||
the binary is absent;
|
||||
8. converge recursive `root:root` source ownership and binary `root:root`
|
||||
mode `0755` with one item-driven `ansible.builtin.file` loop; and
|
||||
9. render `templates/config.yml.j2` to `scriptorium_config_path` as
|
||||
`root:root` mode `0644` with one `ansible.builtin.template` task.
|
||||
5. Invoke `scriptorium_go_binary` with `ansible.builtin.command` and `argv`
|
||||
from `scriptorium_source_dir`. Supply `build`, `-trimpath`, linker flags
|
||||
`-s -w`, the configured output path, and package `./cmd/scriptorium`. Set
|
||||
`CGO_ENABLED=0`, `GOWORK=off`, and `GOTOOLCHAIN=local`. Mark the executed
|
||||
mutation changed as required for an imperative command.
|
||||
6. Put
|
||||
`scriptorium_checkout.changed or not scriptorium_binary.stat.exists`
|
||||
directly on the build task. Register only the Git and binary results needed
|
||||
by that expression.
|
||||
7. Use task-level `become: true` for dependency installation and tasks that
|
||||
manage root-owned state. Do not require play-level privilege escalation.
|
||||
8. Make the template render `{}` when every configuration variable is null.
|
||||
Otherwise conditionally emit the direct directory fields, `server`,
|
||||
`defaults`, and `backends`. Emit `server` and `defaults` only when at least
|
||||
one descendant is non-null. Serialize scalar values and the backend mapping
|
||||
safely, and do not assemble a configuration dictionary in a fact.
|
||||
9. Replace the placeholder README with concise documentation covering the
|
||||
supported platform, fact and privilege requirements, every public variable,
|
||||
dependency opt-out, build triggers, configuration discovery path,
|
||||
null-as-omission behavior, falsey values, the native backend mapping,
|
||||
caller-side composition, credential-name boundary, absent content/service
|
||||
management, and actual integration-test effects.
|
||||
10. Add a baseline integration harness with one `tests/integration` entry
|
||||
point. Use strict shell options, repository-relative paths, a private
|
||||
temporary directory, pinned collection installation, syntax checking,
|
||||
production-profile ansible-lint, and a best-effort cleanup trap that
|
||||
reapplies the default role and removes only its temporary directory.
|
||||
11. In the baseline scenario, converge the default role without play-level
|
||||
escalation. Create a private endpoint-profile fixture and exercise the
|
||||
installed binary with an offline `scriptorium inspect profile` invocation
|
||||
that explicitly loads the managed configuration. Successful native use is
|
||||
sufficient; do not compare application output to a snapshot. Immediately
|
||||
apply the same role again and require aggregate `changed=0` and `failed=0`.
|
||||
12. Do not inspect source commits, ownership, permissions, package state, or
|
||||
the exact default configuration in tests. Those are postconditions of the
|
||||
modules that manage them, not additional role-authored behavior.
|
||||
|
||||
### Validation
|
||||
|
||||
From `ansible-role-scriptorium`, run:
|
||||
|
||||
```text
|
||||
tests/integration
|
||||
git diff --check
|
||||
git status --short
|
||||
```
|
||||
|
||||
Expected result: syntax and lint pass; the default role convergence succeeds;
|
||||
Scriptorium accepts the managed configuration through an offline native
|
||||
profile inspection; the immediate repeat reports `changed=0` and `failed=0`;
|
||||
cleanup leaves a usable default deployment; and only intended Stage 1 files
|
||||
are modified.
|
||||
|
||||
### Completion Criteria
|
||||
|
||||
- The fixed public interface is present in defaults and matching argument
|
||||
specifications.
|
||||
- Production tasks follow the nine-operation roadmap flow without additional
|
||||
probes, private state, handlers, or production self-validation.
|
||||
- The template expresses every native configuration field, renders `{}` by
|
||||
default, omits null values and empty parents, and preserves non-null falsey
|
||||
values.
|
||||
- README, metadata, dependency declaration, task flow, template, and baseline
|
||||
test description agree.
|
||||
- Baseline native smoke testing and clean repeated convergence pass.
|
||||
- No repository outside `ansible-role-scriptorium` changes.
|
||||
|
||||
### Commit
|
||||
|
||||
Commit intent: `Install and configure Scriptorium`
|
||||
|
||||
## Stage 2: Add Focused Behavioral Coverage
|
||||
|
||||
### Objective
|
||||
|
||||
Add representative coverage for missing-binary recovery through caller-owned
|
||||
build tooling and for conditional configuration rendering through
|
||||
Scriptorium's native offline interface.
|
||||
|
||||
### Required Policy Review
|
||||
|
||||
Before beginning this stage, read:
|
||||
|
||||
- `AGENTS.md`
|
||||
- `docs/orientation.md`
|
||||
- `docs/conventions.md`
|
||||
- `docs/ansible.md`
|
||||
- `docs/testing.md`
|
||||
|
||||
### Preconditions
|
||||
|
||||
- Stage 1 is committed and `ansible-role-scriptorium` is otherwise clean.
|
||||
- Stage 1's complete integration entry point passes.
|
||||
- The default managed deployment can be restored by applying the role with no
|
||||
overrides.
|
||||
|
||||
### Owning Repository
|
||||
|
||||
`ansible-role-scriptorium`
|
||||
|
||||
### Required Changes
|
||||
|
||||
1. Retain the baseline default smoke and immediate clean repeat. Extend the
|
||||
existing driver rather than introducing another integration framework or
|
||||
shared cross-role test helper.
|
||||
2. Add one recovery scenario that combines the two related public behaviors:
|
||||
- create an executable temporary wrapper that records its invocation and
|
||||
then delegates to `/snap/bin/go`;
|
||||
- remove `/usr/local/bin/scriptorium`;
|
||||
- apply the role with
|
||||
`scriptorium_manage_build_dependencies: false` and
|
||||
`scriptorium_go_binary` set to that wrapper;
|
||||
- confirm the supplied executable was used;
|
||||
- exercise the installed Scriptorium with the offline native smoke command;
|
||||
and
|
||||
- immediately repeat with the same inputs and require aggregate
|
||||
`changed=0` and `failed=0`.
|
||||
3. Add one representative configuration scenario. Supply a small selection
|
||||
that spans direct and nested fields, including:
|
||||
- a test-owned `scriptorium_profile_dir`;
|
||||
- a non-default `scriptorium_server_addr`;
|
||||
- at least one explicit zero server byte limit;
|
||||
- `scriptorium_defaults_render_format: json`;
|
||||
- selected null directory and server fields that must remain omitted; and
|
||||
- one `scriptorium_backends` entry with an endpoint, zero capacity values,
|
||||
and a false value inside `extra_params`.
|
||||
4. Create a test-owned profile that selects the configured backend. Invoke
|
||||
`scriptorium inspect profile` with the managed configuration and require
|
||||
successful offline inspection without a provider request or credential
|
||||
value.
|
||||
5. Parse the rendered YAML and assert only representative role-authored
|
||||
mappings: the selected direct and nested values are present, the selected
|
||||
null fields are absent, zero and false values survive, and the selected
|
||||
backend ID and definition are available. Do not compare the complete
|
||||
document or verify file metadata.
|
||||
6. Immediately repeat the representative convergence with the same explicit
|
||||
variables and require aggregate `changed=0` and `failed=0`.
|
||||
7. Keep the cleanup trap responsible for restoring the default deployment on
|
||||
success or failure when possible. Test mutation is limited to the three
|
||||
declared role-managed paths and the driver's private temporary directory.
|
||||
8. Do not add an alternate-release scenario, checkout assertion, task-name
|
||||
grep, change-count requirement for mutating runs, metadata-repair scenario,
|
||||
unsupported-platform matrix, or exhaustive case for every variable.
|
||||
9. Update the README validation section so it describes only the implemented
|
||||
static checks, default smoke and clean repeat, combined recovery case, and
|
||||
representative configuration case.
|
||||
|
||||
### Validation
|
||||
|
||||
From `ansible-role-scriptorium`, run:
|
||||
|
||||
```text
|
||||
tests/integration
|
||||
git diff --check
|
||||
git status --short
|
||||
```
|
||||
|
||||
Expected result: static validation and all three behavioral paths pass; the
|
||||
specified repeat runs report `changed=0` and `failed=0`; the supplied Go
|
||||
wrapper is observed during missing-binary recovery; native profile inspection
|
||||
accepts and uses the representative configuration; cleanup restores a usable
|
||||
default deployment; and only intended Stage 2 test and documentation changes
|
||||
remain.
|
||||
|
||||
### Completion Criteria
|
||||
|
||||
- One recovery scenario proves both missing-binary rebuilding and use of the
|
||||
caller-selected Go executable, followed by clean convergence.
|
||||
- One representative configuration proves selected field mapping, null
|
||||
omission, falsey-value preservation, backend pass-through, native acceptance,
|
||||
and clean convergence.
|
||||
- No test snapshots mutable defaults, inspects task traces, or rechecks
|
||||
postconditions owned by trusted modules.
|
||||
- The complete integration entry point passes and its README description is
|
||||
accurate.
|
||||
- No repository outside `ansible-role-scriptorium` changes.
|
||||
|
||||
### Commit
|
||||
|
||||
Commit intent: `Add focused Scriptorium integration coverage`
|
||||
|
||||
## Final Validation and Handoff
|
||||
|
||||
After Stage 2, from `ansible-role-scriptorium`, run:
|
||||
|
||||
```text
|
||||
tests/integration
|
||||
git log --oneline -2
|
||||
git status --short --branch
|
||||
cd ..
|
||||
./scripts/status
|
||||
```
|
||||
|
||||
Report both role commit hashes, the static and behavioral validation results,
|
||||
the clean-repeat evidence, the restored default deployment, any deviation or
|
||||
remaining concern, and any complexity beyond the planned task flow with the
|
||||
acceptance criterion that required it.
|
||||
|
||||
Do not edit workspace planning files, mark Feature 0004 `done`, or push during
|
||||
out-of-band implementation. Completion requires subsequent independent review.
|
||||
|
||||
## Progress Notes
|
||||
|
||||
No stages have been completed.
|
||||
|
||||
@@ -4,7 +4,8 @@ Last updated: 2026-09-09
|
||||
|
||||
Current milestone: MVP standalone application roles
|
||||
|
||||
Current focus: Select the next unit of work.
|
||||
Current focus: Execute the approved Feature 0004 Scriptorium implementation
|
||||
plan.
|
||||
|
||||
| ID | Deliverable | Owner | State | Evidence or next step |
|
||||
| --- | --- | --- | --- | --- |
|
||||
@@ -14,12 +15,13 @@ Current focus: Select the next unit of work.
|
||||
| M0-04 | Aggregate repository status | workspace | done | `scripts/status` created |
|
||||
| M0-05 | Suite-level role repository | ansible-role-dnd-pipeline | done | Repository created and added to `workspace.yml` |
|
||||
| M1-01 | Remaining MVP design decisions | workspace | done | Feature 0003 roadmap fixes the Notarius configuration interface |
|
||||
| M1-02 | Standalone application roles | role repositories | in_progress | Select the next standalone application role feature |
|
||||
| M1-02 | Standalone application roles | role repositories | in_progress | Implement Feature 0004 in `ansible-role-scriptorium` |
|
||||
| M1-03 | Suite-level pipeline role | ansible-role-dnd-pipeline | proposed | Begin after standalone role interfaces stabilize |
|
||||
| M1-04 | Collection assembly | ansible-collection-dnd | proposed | Select and implement the pipeline-role incorporation mechanism |
|
||||
| F0001 | Install Seriatim from pinned source | ansible-role-seriatim | done | Implementation, integration coverage, and final remediation completed through `fb8dc41` |
|
||||
| F0002 | Install and configure Audita | ansible-role-audita | done | Field-specific configuration completed in `e61f02f` and `847c4c3`; full suite passed |
|
||||
| F0003 | Install and configure Notarius | ansible-role-notarius | done | Implemented and validated through remediation commit `eb4f511` |
|
||||
| F0004 | Install and configure Scriptorium | ansible-role-scriptorium | ready | Execute `docs/roadmap/implementation.md` |
|
||||
| A0001 | Audit completed standalone-role test suites | workspace and standalone roles | done | Findings recorded in `docs/roadmap/test-suite-audit-findings.md` |
|
||||
| R0001 | Simplify completed standalone-role test suites | workspace and standalone roles | done | Role commits `e08c63c`, `d2d0b9d`, and `cfecf37` passed implementation and independent review |
|
||||
|
||||
|
||||
Reference in New Issue
Block a user