From bbeecc0055abff45f8bcff47a2510d83089499dd Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Wed, 9 Sep 2026 14:20:26 +0000 Subject: [PATCH] Plan the Scriptorium role implementation --- ...m-source-installation-and-configuration.md | 331 +++++++++++++++ docs/roadmap/implementation.md | 382 +++++++++++++++++- docs/status.md | 6 +- 3 files changed, 709 insertions(+), 10 deletions(-) create mode 100644 docs/roadmap/features/0004-scriptorium-source-installation-and-configuration.md diff --git a/docs/roadmap/features/0004-scriptorium-source-installation-and-configuration.md b/docs/roadmap/features/0004-scriptorium-source-installation-and-configuration.md new file mode 100644 index 0000000..0b69721 --- /dev/null +++ b/docs/roadmap/features/0004-scriptorium-source-installation-and-configuration.md @@ -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. diff --git a/docs/roadmap/implementation.md b/docs/roadmap/implementation.md index b66b2eb..0337004 100644 --- a/docs/roadmap/implementation.md +++ b/docs/roadmap/implementation.md @@ -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. diff --git a/docs/status.md b/docs/status.md index a437488..256acd8 100644 --- a/docs/status.md +++ b/docs/status.md @@ -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 |