Complete the Scriptorium feature

This commit is contained in:
2026-09-09 19:17:08 +00:00
parent 6034691350
commit 860651966a
5 changed files with 70 additions and 433 deletions

View File

@@ -1,6 +1,6 @@
# Feature 0004: Scriptorium Source Installation and Configuration
Status: ready
Status: done
## Summary
@@ -313,10 +313,14 @@ 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.
With this feature complete, the Narratio standalone role can be designed
against all four downstream application-role interfaces. The later pipeline
role will install maintained prompt-library content and supply D&D-specific
Scriptorium configuration through this role.
The role was implemented in `ansible-role-scriptorium` commits `90d7f45` and
`5944eac`, then finalized after independent review by remediation commit
`8f47aa8`. Its complete integration suite passes.
## Potential Future Hardening

View File

@@ -1,380 +1,14 @@
# Implementation Plan 0004: Install and Configure Scriptorium
# Implementation Plan
Status: ready
Status: pending
Target executor: GPT-5.6-Terra
No implementation plan is currently active. Feature 0004 was implemented in
`ansible-role-scriptorium` commits `90d7f45` and `5944eac`, independently
reviewed, and finalized by remediation commit `8f47aa8`.
Feature roadmap:
[Feature 0004: Scriptorium Source Installation and Configuration](features/0004-scriptorium-source-installation-and-configuration.md)
Its durable scope and rationale remain in
[the Feature 0004 roadmap](features/0004-scriptorium-source-installation-and-configuration.md),
and its final review record remains in [the remediation record](remediation.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
- [x] Stage 1 complete — `ansible-role-scriptorium` commit `90d7f45`; full
`tests/integration` passed. No deviations.
- [x] Stage 2 complete — `ansible-role-scriptorium` commit `5944eac`; full
`tests/integration` passed. No deviations.
Select and approve the next unit of work before replacing this notice with a
new staged implementation plan.

View File

@@ -242,10 +242,11 @@ The following areas require investigation and decisions before implementation:
- Standalone role Galaxy names and the dependency-manifest/bootstrap layout
- The mechanism used to incorporate the independently developed pipeline role
into collection releases
- Configuration contracts and filesystem requirements of Notarius, Audita,
Seriatim, and Scriptorium
- Narratio's application configuration contract and runtime filesystem
requirements
- The prompt-library destination and its integration with Scriptorium
- Secret injection and storage
- Operator-group membership variables and directory permission details
- Upgrade, downgrade, and removal behavior
- Ansible dependency, validation, and test strategy
- Collection bootstrap and end-to-end validation details beyond the locked
dependency and testing policies above

View File

@@ -1,64 +1,63 @@
# Feature 0003 Remediation Review
# Feature 0004 Remediation Review
Status: resolved
Reviewed implementation commits:
- `287fc4a Install and configure Notarius`
- `733a194 Add focused Notarius integration coverage`
- `90d7f45 Install and configure Scriptorium`
- `5944eac Add focused Scriptorium integration coverage`
Remediation commit:
- `eb4f511 Correct Notarius metadata and dependency coverage`
- `8f47aa8 Clean up Scriptorium review findings`
## Finding 1: Correct the Galaxy License Metadata
The production role implements the targeted architecture. The complete
integration suite, independent syntax and lint checks, Bash syntax validation,
native default-configuration inspection, and clean-repeat scenarios pass. No
production behavior or architectural gap requires remediation.
Severity: medium
`ansible-role-notarius/meta/main.yml` declared `license: BSD-3-Clause`, while
the retained repository license is CC0 1.0 Universal.
### Resolution
The Galaxy license identifier is now `CC0-1.0`, consistent with the repository
license.
## Finding 2: Remove the Redundant Build Change Override
Status: withdrawn
The initial review treated `changed_when: true` on the conditionally executed
build command as redundant. Validation after removing it failed ansible-lint's
`no-changed-when` production rule.
### Resolution
The directive remains in place. Unlike a desired-state module, the imperative
build command must explicitly declare its change semantics for lint. The
existing `when` expression still determines whether the command runs, and
`changed_when: true` accurately reports that every executed build changes the
binary. The distinction is now documented in `docs/ansible.md`.
## Finding 3: Make the Dependency-Opt-Out Test Prove the Selected Go Binary
## Finding 1: Remove the Retained Placeholder README Sentence
Severity: low
The original integration test pointed `notarius_go_binary` at a symlink to
`/snap/bin/go`, so it did not prove that the configured path was actually
invoked.
`ansible-role-scriptorium/README.md` ends with the original placeholder text,
“Ansible role to install the Scriptorium application.” The rewritten README
already provides a complete description, so this trailing sentence is an
accidental duplicate left by the initial replacement.
### Resolution
The test now supplies a wrapper in its private temporary directory. The
wrapper records its invocation before delegating to `/snap/bin/go`, and the
suite asserts that the record exists after the dependency-opt-out build.
The trailing placeholder sentence was deleted without changing the completed
role or validation documentation.
## Finding 2: Simplify Representative Test Data Flow
Severity: low
`ansible-role-scriptorium/tests/integration` repeats the same long
representative extra-vars document for the initial convergence and its clean
repeat. This makes the requirement that both runs use identical inputs harder
to see and creates two values that can drift independently.
`ansible-role-scriptorium/tests/verify-config.yml` also persists the parsed
configuration with a separate `ansible.builtin.set_fact` task even though the
value is needed only by the following assertion. This adds a task and mutable
test state without adding behavior.
### Resolution
The shell driver now defines the representative extra-variable arguments once
and passes the same array to both convergence calls. The verifier parses the
slurped configuration through a variable local to its assertion task, and the
standalone `set_fact` task was removed. Existing assertions, native inspection,
and clean-repeat coverage remain unchanged.
## Final Validation
The complete `tests/integration` suite passed after remediation with Ansible
Core 2.21.3 and ansible-lint 26.8.0. Production-profile lint passed, every
selected build and configuration behavior remained valid, all required repeat
runs were clean, and the suite restored Notarius `v0.6.0` with the minimal
`version: 4` configuration.
`git diff --check` also passed. No unresolved Feature 0003 findings remain.
`bash -n tests/integration`, the complete `tests/integration` suite, and
`git diff --check` passed after remediation. Production-profile lint and
Ansible syntax checks passed within the integration entry point. Default,
missing-binary recovery, and representative-configuration scenarios all
passed their native inspections and immediate clean repeats, and cleanup
restored the usable `{}` default configuration. No unresolved Feature 0004
findings remain.

View File

@@ -4,8 +4,7 @@ Last updated: 2026-09-09
Current milestone: MVP standalone application roles
Current focus: Execute the approved Feature 0004 Scriptorium implementation
plan.
Current focus: Select the next unit of work.
| ID | Deliverable | Owner | State | Evidence or next step |
| --- | --- | --- | --- | --- |
@@ -15,13 +14,13 @@ plan.
| 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 | Implement Feature 0004 in `ansible-role-scriptorium` |
| M1-02 | Standalone application roles | role repositories | in_progress | Select the Narratio standalone-role feature |
| 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` |
| F0004 | Install and configure Scriptorium | ansible-role-scriptorium | done | Implemented and validated through remediation commit `8f47aa8` |
| 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 |