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

417 lines
19 KiB
Markdown

# Implementation Plan 0005: Install and Configure Narratio
Status: ready
Target executor: GPT-5.6-Terra
Feature roadmap:
[Feature 0005: Narratio Source Installation and Configuration](features/0005-narratio-source-installation-and-configuration.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.