Files
ansible-workspace-dnd/docs/roadmap/implementation.md

17 KiB

Implementation Plan 0004: Install and Configure Scriptorium

Status: ready

Target executor: GPT-5.6-Terra

Feature roadmap: Feature 0004: Scriptorium Source Installation and Configuration

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:

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:

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:

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

  • Stage 1 complete — ansible-role-scriptorium commit 90d7f45; full tests/integration passed. No deviations.