Complete the Narratio role feature

This commit is contained in:
2026-09-09 22:27:52 +00:00
parent e418edc5ef
commit 8e34f56f81
4 changed files with 63 additions and 453 deletions

View File

@@ -1,6 +1,6 @@
# Feature 0005: Narratio Source Installation and Configuration
Status: ready
Status: done
## Summary
@@ -441,11 +441,15 @@ their configuration, campaign and session inputs, prompt content, and any
selected object-storage or model credentials. Those remain caller
responsibilities for this standalone feature.
After this feature, all five standalone application roles will have stable
With this feature complete, all five standalone application roles have stable
public interfaces. The next unit can design the suite-level pipeline role,
including dependency composition, compatible version selection, D&D pipeline
policy, campaign and prompt deployment, secrets, and operator group membership.
The role was implemented in `ansible-role-narratio` commits `3be78d8` and
`0e8ad9f`, then finalized after independent review by remediation commit
`7feff47`. Its complete integration suite passes.
## Potential Future Hardening
- Build into a temporary path before replacing the installed executable.

View File

@@ -1,416 +1,14 @@
# Implementation Plan 0005: Install and Configure Narratio
# Implementation Plan
Status: ready
Status: pending
Target executor: GPT-5.6-Terra
No implementation plan is currently active. Feature 0005 was implemented in
`ansible-role-narratio` commits `3be78d8` and `0e8ad9f`, independently
reviewed, and finalized by remediation commit `7feff47`.
Feature roadmap:
[Feature 0005: Narratio Source Installation and Configuration](features/0005-narratio-source-installation-and-configuration.md)
Its durable scope and rationale remain in
[the Feature 0005 roadmap](features/0005-narratio-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 independently usable `rakestraw.narratio` role defined by
Feature 0005. Install Narratio from its pinned source release, build its
executable on the managed host, create its application account and managed
filesystem roots, and render its native root pipeline configuration through
the approved field-specific interface.
The feature roadmap remains authoritative for scope, rationale, target end
state, public variables, ownership, modes, 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-narratio`. Treat this workspace, `../narratio`,
`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.
- Start from the feature roadmap's ten-step module-driven task flow. Trust
`apt`, Snap, `group`, `user`, `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 Narratio from production role tasks. Native version and
configuration verification belong 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.
- Treat each roadmap-designated open-ended collection as one caller-supplied
native value. Do not define variables for caller-selected keys or implement
role-side loading, merging, or precedence.
- Require a WhisperX transcription URL. Do not invent a default endpoint or
weaken the upstream requirement.
- 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 the currently absent managed source, binary, pipeline file, account,
and default directory roots as disposable integration state. Never delete
caller content under those roots during testing. Restore the baseline role
deployment through test cleanup.
## Starting State and Preconditions
- `ansible-role-narratio` is clean on `main` at
`b97a317f9ec9f15675e81c65af3f4a1ad8176886`, tracking `origin/main` without
divergence.
- This plan, the ready Feature 0005 roadmap, and the corresponding MVP/status
updates are committed before out-of-band execution begins.
- `/usr/local/src/narratio`, `/usr/local/bin/narratio`,
`/usr/local/etc/narratio/pipeline.yml`, `/var/lib/narratio`,
`/var/spool/narratio`, `/var/cache/narratio`, and
`/usr/local/share/narratio/campaigns` 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 task-level 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 lightweight tag `v2.0.0` resolves to
`ed4623bc415901134c765fe9cf06d59aa7891500` and declares Go 1.26.0.
- The native build uses package `./cmd/narratio`, `CGO_ENABLED=0`,
`GOWORK=off`, `GOTOOLCHAIN=local`, `-trimpath`, and linker flags
`-s -w -X gitea.maximumdirect.net/eric/narratio/internal/buildinfo.Version=<version>`.
- `narratio version` reports the embedded release, and
`narratio config validate --config <path>` validates the root pipeline
without loading a session or contacting WhisperX when the configuration
contains no credential references or campaign-driven artifact families.
- A pipeline requires an absolute HTTP or HTTPS WhisperX transcription URL.
All other values needed by a baseline pipeline are supplied by Narratio's
native defaults.
## Repository Scope
Repository that changes:
- `ansible-role-narratio`: role implementation, public interface, account and
filesystem management, 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.
- `../narratio`: authoritative release, build, CLI, configuration, filesystem,
and integration 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 Narratio, creates its account
and managed directories, renders the baseline native pipeline, documents its
fixed public interface, and demonstrates native validity and clean repeated
convergence.
### 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-narratio` has no uncommitted changes.
- The workspace planning commit containing this plan exists.
- The managed paths listed in Starting State remain free of data that must be
preserved.
### Owning Repository
`ansible-role-narratio`
### 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/pipeline.yml.j2`,
`collections/requirements.yml`, `ansible.cfg`, and focused files under
`tests/`.
2. Declare role name `narratio`, 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 every variable in the Feature 0005 Fixed Public Interface exactly
once in role defaults, except the required
`narratio_whisperx_transcribe_url`, which has no invented default. Keep the
fourteen installation, account, and managed-filesystem defaults concrete.
Give every other optional pipeline field the YAML value `null`.
4. Mirror the complete public interface in `meta/argument_specs.yml`:
- mark `narratio_whisperx_transcribe_url` as a required string;
- use `path` for source, binary, Go executable, configuration, and managed
root paths;
- use `bool`, `int`, `float`, `str`, `list`, and `dict` according to each
native field's documented shape;
- require every concrete installation, account, and filesystem variable;
and
- leave nullable application settings optional.
Do not duplicate Narratio's semantic validation rules or deeply restate its
schemas for caller-supplied collections.
5. 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`, and install the classic Go Snap from
`narratio_go_snap_channel` when dependency management is enabled;
3. create `narratio_group` as a system group and `narratio_user` as a system
account in that group, with `narratio_workspace_root` as its home,
`create_home: false`, and `/usr/sbin/nologin` as its shell;
4. create source, binary, configuration, runtime, and campaign parent/root
directories with the roadmap's ownership and modes using the smallest
clear number of `ansible.builtin.file` tasks and item loops;
5. check out `narratio_version` with `ansible.builtin.git`, `force: true`,
and register its module result;
6. inspect only `narratio_binary_path` with `ansible.builtin.stat`;
7. build directly to `narratio_binary_path` when the checkout changed or
the binary is absent;
8. converge recursive root ownership of the source tree and root ownership
plus mode `0755` on the binary with `ansible.builtin.file`; and
9. render `templates/pipeline.yml.j2` to `narratio_config_path` as
`root:{{ narratio_group }}` mode `0640` with one
`ansible.builtin.template` task.
6. Invoke `narratio_go_binary` with `ansible.builtin.command` and `argv` from
`narratio_source_dir`. Supply `build`, `-trimpath`, the exact version linker
flags, the configured output path, and package `./cmd/narratio`. Set
`CGO_ENABLED=0`, `GOWORK=off`, and `GOTOOLCHAIN=local`. Mark the executed
mutation changed as required for an imperative command.
7. Put
`narratio_checkout.changed or not narratio_binary.stat.exists` directly on
the build task. Register only the Git and binary results needed by that
expression.
8. Use task-level `become: true` for dependency installation, account changes,
and tasks that manage privileged state. Do not require play-level privilege
escalation.
9. Make the template always render the required WhisperX URL and the four
role-owned filesystem roots. Conditionally emit all other fields from the
roadmap interface. Use `is not none` for every optional field, omit parent
sections with no selected descendants, preserve non-null falsey values, and
serialize strings and collections safely. Do not assemble a configuration
dictionary in facts.
10. Treat ConfigKit imports, publish outputs, Notarius references and outputs,
and Scriptorium artifacts and artifact families as complete native caller
values. Render them without structural rewriting. Do not load external
files or implement merge behavior.
11. Replace the placeholder README with concise documentation covering the
supported platform, fact and privilege requirements, every public variable,
required WhisperX URL, dependency opt-out, build triggers, application
account, managed directory ownership, configuration discovery path,
null-as-omission behavior, falsey values, caller-side composition of native
collections, credential-name boundary, externally managed imports and
profiles, suite-role boundary, and actual integration-test effects.
12. 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 baseline role inputs and removes only its private temporary
directory.
13. In the baseline scenario, explicitly supply
`narratio_whisperx_transcribe_url` with a syntactically valid local test URL
that is never contacted. Converge without play-level escalation, run
`narratio config validate --config <managed-path>` as `narratio_user`, and
immediately apply the same role again with aggregate `changed=0` and
`failed=0`.
14. Do not inspect account records, directory or file metadata, source commits,
package state, the template task result, or the complete rendered document
in tests. Those are postconditions of the modules that manage them, not
additional role-authored behavior.
### Validation
From `ansible-role-narratio`, run:
```text
tests/integration
git diff --check
git status --short
```
Expected result: syntax and lint pass; baseline convergence succeeds; Narratio
validates the managed pipeline while running as its application account; the
immediate repeat reports `changed=0` and `failed=0`; cleanup leaves a usable
baseline deployment; and only intended Stage 1 files are modified.
### Completion Criteria
- The fixed public interface is present in defaults and matching argument
specifications, with only the deployment-specific WhisperX URL required.
- Production tasks follow the roadmap flow without additional probes, private
state, handlers, or production self-validation.
- Account and directory management implements the fixed ownership and access
boundary without selecting human operators.
- The template expresses every native root-pipeline field, omits null values
and empty optional parents, and preserves non-null falsey values.
- README, metadata, dependency declaration, task flow, template, and baseline
test description agree.
- Baseline native configuration validation and clean repeated convergence pass.
- No repository outside `ansible-role-narratio` changes.
### Commit
Commit intent: `Install and configure Narratio`
## Stage 2: Add Focused Behavioral Coverage
### Objective
Add representative coverage for caller-selected release identity,
missing-binary recovery through caller-owned build tooling, and the complete
configuration interface's omission, falsey-value, and composition behavior.
### 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-narratio` is otherwise clean.
- Stage 1's complete integration entry point passes.
- The baseline managed deployment can be restored by applying the role with
the test WhisperX URL and no optional application overrides.
### Owning Repository
`ansible-role-narratio`
### Required Changes
1. Retain the baseline native validation and immediate clean repeat. Extend the
existing driver rather than introducing another integration framework or
shared cross-role test helper.
2. Make the selected-version behavior explicit in one convergence by setting
`narratio_version: v2.0.0`, then execute `narratio version` and require
`narratio v2.0.0`. Do not assert or parse the role's default value and do not
inspect the Git checkout.
3. 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/narratio`;
- apply the role with `narratio_manage_build_dependencies: false` and
`narratio_go_binary` set to that wrapper;
- confirm the supplied executable was used;
- exercise the installed Narratio with `version` and native configuration
validation; and
- immediately repeat with the same inputs and require aggregate
`changed=0` and `failed=0`.
4. Add one representative configuration scenario using a small valid subset
spanning direct and nested fields. Include selected zero, false, empty
string, empty list, or empty mapping values where Narratio accepts them,
selected null fields that must remain absent, and at least one open-ended
collection assembled from independently defined inventory/play variables
before assignment.
5. Keep the representative pipeline independent of external services,
credentials, imported files, profile overlays, sessions, and campaigns.
Choose collection entries valid under the upstream `v2.0.0` contract and
avoid artifact families, which require campaign-owned party input during
native validation.
6. Parse the rendered authoring YAML and assert only representative
role-authored mappings: selected fields are present at their correct native
paths, selected null fields and their otherwise-empty parents are absent,
intentional falsey values survive, and the composed collection retains its
caller-supplied entries. Place any decoded-document expression directly in
the assertion task's `vars`; do not add a separate assembly or verification
fact.
7. Run `narratio config validate --config <managed-path>` as `narratio_user`
for native acceptance without a network request. Immediately repeat the
representative convergence with identical public inputs and require
aggregate `changed=0` and `failed=0`.
8. Keep the cleanup trap responsible for restoring the baseline deployment on
success or failure when possible. Test mutation is limited to the declared
role-managed paths and the driver's private temporary directory; do not
delete or replace content below a runtime or campaign root.
9. Do not add a second release, task-name grep, change-count requirement for
mutating runs, metadata-repair scenario, unsupported-platform matrix,
service invocation, full configuration snapshot, or exhaustive case for
every variable.
10. Update the README validation section so it describes only the implemented
static checks, baseline native validation and clean repeat,
selected-version assertion, combined recovery case, and representative
configuration case.
### Validation
From `ansible-role-narratio`, run:
```text
tests/integration
git diff --check
git status --short
```
Expected result: static validation and all behavioral paths pass; the selected
release reports `v2.0.0`; the supplied Go wrapper is observed during
missing-binary recovery; Narratio natively accepts both baseline and
representative managed pipelines as its application account; the specified
repeat runs report `changed=0` and `failed=0`; cleanup restores a usable
baseline deployment; and only intended Stage 2 test and documentation changes
remain.
### Completion Criteria
- One explicitly selected-version case proves caller-controlled source release
installation through Narratio's native version output.
- 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, caller-side collection composition,
native acceptance, application-account access, 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-narratio` changes.
### Commit
Commit intent: `Add focused Narratio integration coverage`
## Final Validation and Handoff
After Stage 2, from `ansible-role-narratio`, run:
```text
tests/integration
git log --oneline -2
git status --short --branch
cd ..
./scripts/status
```
Report both role commit hashes, static and behavioral validation results,
clean-repeat evidence, the restored baseline 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 0005 `done`, or push during
out-of-band implementation. Completion requires subsequent independent review.
## Progress Notes
- [x] Stage 1 complete — `ansible-role-narratio` commit `3be78d8`; full
`tests/integration` passed. No deviations.
- [x] Stage 2 complete — `ansible-role-narratio` commit `0e8ad9f`; 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

@@ -1,63 +1,71 @@
# Feature 0004 Remediation Review
# Feature 0005 Remediation Review
Status: resolved
Reviewed implementation commits:
- `90d7f45 Install and configure Scriptorium`
- `5944eac Add focused Scriptorium integration coverage`
- `3be78d8 Install and configure Narratio`
- `0e8ad9f Add focused Narratio integration coverage`
Remediation commit:
- `8f47aa8 Clean up Scriptorium review findings`
- `7feff47 Clean up Narratio review findings`
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.
The production role implements the targeted architecture, and the complete
integration suite passes. The findings were limited to residual documentation
and test-quality issues; no production task, template, default, argument
specification, metadata, or dependency required a change.
## Finding 1: Remove the Retained Placeholder README Sentence
Severity: low
`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.
`ansible-role-narratio/README.md` ended with the original placeholder sentence,
“Ansible role to install the Narratio D&D transcript and session report
generation suite.” The rewritten README already provided the complete role
description, so this was accidental residue from the initial repository.
### Resolution
The trailing placeholder sentence was deleted without changing the completed
role or validation documentation.
The retained placeholder sentence was removed without changing the substantive
role documentation.
## Finding 2: Simplify Representative Test Data Flow
## Finding 2: Make the Composition Test Non-vacuous
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.
`ansible-role-narratio/tests/representative.yml` claimed to exercise
caller-side composition, but both secondary fragments were empty. The verifier
consequently proved only that one original entry survived serialization.
### 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.
The Scriptorium artifact map now combines two nonempty caller-defined
fragments, and the verifier checks one entry from each. Publish outputs use one
direct value so the suite retains only one representative composition case.
Native validation and the immediate clean repeat remain in place.
## Finding 3: Use Ansible Privilege Escalation Directly in Verifiers
Severity: low
Both configuration verifiers escalated to root and then executed
`/usr/bin/sudo -u narratio` inside `ansible.builtin.command`, creating a
redundant second privilege transition.
### Resolution
Both verifier tasks now use `become_user: narratio` and invoke Narratio
directly. The integration driver enables pipelining and uses `/tmp` for
Ansible's transient module area, avoiding the local controller's otherwise
unmet temporary-file ACL requirement without adding a nested sudo command.
## Final Validation
`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.
`git diff --check` passed after remediation. Syntax checking and
production-profile lint passed within the integration entry point. Baseline,
missing-binary recovery, and representative-configuration scenarios passed
their native Narratio checks as the application account, every required
immediate repeat reported `changed=0` and `failed=0`, and cleanup restored the
usable baseline deployment. No unresolved Feature 0005 findings remain.

View File

@@ -2,9 +2,9 @@
Last updated: 2026-09-09
Current milestone: MVP standalone application roles
Current milestone: Select the next MVP unit of work
Current focus: Implement Feature 0005 for the standalone Narratio role.
Current focus: Select the next unit of work.
| ID | Deliverable | Owner | State | Evidence or next step |
| --- | --- | --- | --- | --- |
@@ -14,14 +14,14 @@ Current focus: Implement Feature 0005 for the standalone Narratio role.
| 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 and review Feature 0005 |
| M1-02 | Standalone application roles | role repositories | done | All five standalone roles implemented and independently reviewed |
| 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 | done | Implemented and validated through remediation commit `8f47aa8` |
| F0005 | Install and configure Narratio | ansible-role-narratio | ready | Execute `docs/roadmap/implementation.md` |
| F0005 | Install and configure Narratio | ansible-role-narratio | done | Implemented and validated through remediation commit `7feff47` |
| 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 |