Plan the standalone Narratio role
This commit is contained in:
@@ -0,0 +1,461 @@
|
|||||||
|
# Feature 0005: Narratio Source Installation and Configuration
|
||||||
|
|
||||||
|
Status: ready
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
Provide a small, independently usable `rakestraw.narratio` role that installs
|
||||||
|
Narratio from a pinned Git tag, builds its executable on the managed host,
|
||||||
|
creates the application account and runtime directories, and manages its
|
||||||
|
native pipeline configuration. Keep the role application-specific so the
|
||||||
|
later suite role can supply D&D policy through its public interface without
|
||||||
|
making the Narratio role responsible for the other applications or campaign
|
||||||
|
content.
|
||||||
|
|
||||||
|
## Problem or Opportunity
|
||||||
|
|
||||||
|
The `ansible-role-narratio` repository contains only its initial README and
|
||||||
|
license. Narratio is the command-line orchestrator for the transcript pipeline,
|
||||||
|
so the complete suite cannot be composed until Narratio has an independently
|
||||||
|
usable installation and configuration role.
|
||||||
|
|
||||||
|
The four completed downstream application roles establish the concise
|
||||||
|
source-build pattern. Narratio can reuse that pattern, but its native contract
|
||||||
|
also requires a deployment account, shared writable runtime areas, a campaign
|
||||||
|
registry root, and a substantially larger pipeline configuration interface.
|
||||||
|
This feature fixes those boundaries before suite composition begins.
|
||||||
|
|
||||||
|
## 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-narratio` 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 Narratio tag `v2.0.0` by default under
|
||||||
|
`/usr/local/src/narratio`.
|
||||||
|
- Build the native executable with Narratio's release build contract and
|
||||||
|
install it at `/usr/local/bin/narratio`.
|
||||||
|
- Rebuild when the Git checkout changes or the binary is absent.
|
||||||
|
- Converge the source tree and installed binary to root ownership, with the
|
||||||
|
binary at mode `0755`.
|
||||||
|
- Create the `narratio` system group and system account with
|
||||||
|
`/var/lib/narratio` as its home and a non-login shell.
|
||||||
|
- Create the workspace, spool, and cache roots as group-writable,
|
||||||
|
setgid directories owned by `narratio:narratio`.
|
||||||
|
- Create the campaign registry as a root-managed, operator-readable content
|
||||||
|
root; leave its contents to the suite role or another caller.
|
||||||
|
- Manage `/usr/local/etc/narratio/pipeline.yml` as `root:narratio` with mode
|
||||||
|
`0640` using one role-owned template.
|
||||||
|
- Require the caller to supply the deployment-specific WhisperX transcription
|
||||||
|
URL because Narratio has no valid universal value for this required field.
|
||||||
|
- Give filesystem roots concrete role-owned defaults. Default other optional
|
||||||
|
application settings to null and omit them until a caller supplies a
|
||||||
|
non-null value, allowing Narratio to retain ownership of its built-in
|
||||||
|
defaults.
|
||||||
|
- Expose scalar fields individually. Expose native open-ended collections as
|
||||||
|
one variable each, including publish outputs, Notarius references and
|
||||||
|
outputs, and Scriptorium artifacts and artifact families.
|
||||||
|
- Permit callers to compose those collection variables in inventory before
|
||||||
|
assigning the final value; do not add role-owned merge or precedence logic.
|
||||||
|
- Expose ConfigKit's explicit import list in the root document without taking
|
||||||
|
ownership of the imported fragments or named profile overlays.
|
||||||
|
- Keep defaults, argument specifications, README, metadata, collection
|
||||||
|
dependency declaration, template, and focused integration tests aligned
|
||||||
|
with this contract.
|
||||||
|
|
||||||
|
## Non-goals
|
||||||
|
|
||||||
|
- Installing or configuring Audita, Notarius, Seriatim, Scriptorium, WhisperX,
|
||||||
|
or the complete D&D suite.
|
||||||
|
- Populating campaign data, prompt libraries, profiles, schemas, or session
|
||||||
|
documents.
|
||||||
|
- Selecting D&D-specific pipelines, campaigns, models, prompts, artifacts,
|
||||||
|
extraction lanes, publish rules, storage providers, or application paths.
|
||||||
|
- Managing ConfigKit import fragments or named profile overlay files.
|
||||||
|
- Managing credentials, secret values, a secrets directory, environment
|
||||||
|
injection, or the pipeline-wide secret-delivery policy.
|
||||||
|
- Adding human operator accounts to the `narratio` group; the suite role owns
|
||||||
|
operator selection and membership.
|
||||||
|
- A Narratio daemon, service unit, scheduler, or timer.
|
||||||
|
- A raw mapping for the complete pipeline document, a template override, or a
|
||||||
|
second parallel configuration interface.
|
||||||
|
- Copying Narratio's built-in defaults into role defaults merely to render
|
||||||
|
them back into YAML.
|
||||||
|
- Decomposing open-ended artifact, artifact-family, reference, or output maps
|
||||||
|
into variables for caller-defined keys.
|
||||||
|
- Installing from published binary artifacts, cross-compiling, or supporting
|
||||||
|
platforms outside the MVP.
|
||||||
|
- Production execution of Narratio 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 the required
|
||||||
|
`narratio_whisperx_transcribe_url` installs or confirms Git and Go, creates the
|
||||||
|
Narratio account and filesystem roots, checks out Narratio `v2.0.0` at
|
||||||
|
`/usr/local/src/narratio`, builds `/usr/local/bin/narratio`, and manages
|
||||||
|
`/usr/local/etc/narratio/pipeline.yml`.
|
||||||
|
|
||||||
|
The baseline managed pipeline document contains only the caller's required
|
||||||
|
WhisperX URL and the filesystem roots the role deliberately owns. Narratio
|
||||||
|
continues to supply all other application defaults. When a caller sets an
|
||||||
|
optional configuration variable, the corresponding native YAML field appears
|
||||||
|
in the correct section. Null fields and parent mappings with no selected
|
||||||
|
descendants are absent. Explicit false, zero, empty string, empty list, empty
|
||||||
|
mapping, and other non-null values are serialized rather than silently
|
||||||
|
omitted, leaving their native semantics to Narratio.
|
||||||
|
|
||||||
|
Native open-ended collections pass through without structural rewriting.
|
||||||
|
Callers and composing roles may define fragments in separate inventory files
|
||||||
|
and combine them with ordinary Ansible/Jinja expressions before assigning the
|
||||||
|
final collection variable. The role does not merge fragments or define
|
||||||
|
precedence among them.
|
||||||
|
|
||||||
|
The `narratio` account owns the mutable workspace, spool, and cache roots.
|
||||||
|
Their setgid group-write permissions support later direct invocation by human
|
||||||
|
operators whom the suite role adds to the `narratio` group. The root-managed
|
||||||
|
campaign registry remains readable but not writable by those operators.
|
||||||
|
|
||||||
|
An immediate repeated application reports no changes. A changed checkout or a
|
||||||
|
missing binary causes the build command to run. Account, directory, and
|
||||||
|
configuration changes converge through their responsible Ansible modules and
|
||||||
|
do not become build triggers.
|
||||||
|
|
||||||
|
## Repository Ownership
|
||||||
|
|
||||||
|
- `ansible-role-narratio` owns the role implementation, application account,
|
||||||
|
runtime and campaign-root state, public variables, native root pipeline
|
||||||
|
serialization, metadata, dependency declaration, documentation, and
|
||||||
|
repository-local tests.
|
||||||
|
- `../narratio` is the read-only authority for release, build, CLI,
|
||||||
|
configuration, filesystem, and integration contracts.
|
||||||
|
- `rakestraw-ansible` is a read-only style and historical deployment reference.
|
||||||
|
- `ansible-role-dnd-pipeline` will later own compatible application versions,
|
||||||
|
D&D-specific Narratio values, campaign and prompt content, secret delivery,
|
||||||
|
and human operator membership through the standalone roles' public
|
||||||
|
interfaces.
|
||||||
|
- `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 lightweight tag `v2.0.0`, resolving to
|
||||||
|
commit `ed4623bc415901134c765fe9cf06d59aa7891500`.
|
||||||
|
- The release declares Go 1.26.0. The suite-standard `1.26/stable` Go Snap
|
||||||
|
channel satisfies that requirement.
|
||||||
|
- 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 selected tag and provides the native
|
||||||
|
observable for an explicitly requested version in integration tests.
|
||||||
|
- Narratio discovers `/usr/local/etc/narratio/pipeline.yml` before
|
||||||
|
`/etc/narratio/pipeline.yml`; the role manages the first path by default.
|
||||||
|
- `whisperx.transcribe_url` is the only pipeline field without an application
|
||||||
|
default that is required for pipeline validation. The role therefore exposes
|
||||||
|
`narratio_whisperx_transcribe_url` as a required public input rather than
|
||||||
|
inventing a deployment endpoint.
|
||||||
|
- Workspace, campaign, spool, and cache roots are concrete role defaults
|
||||||
|
because the role creates and owns the corresponding filesystem state. These
|
||||||
|
are intentional deployment policy, not a duplicate database of application
|
||||||
|
defaults.
|
||||||
|
- All other optional configuration 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 collections are serialized with Ansible or Jinja filters rather
|
||||||
|
than hand-written YAML quoting.
|
||||||
|
- `narratio_publish_outputs`, `narratio_notarius_references`,
|
||||||
|
`narratio_notarius_outputs`, `narratio_scriptorium_artifacts`, and
|
||||||
|
`narratio_scriptorium_artifact_families` represent their complete native
|
||||||
|
fields. Caller-side composition is permitted, but the role supplies no merge
|
||||||
|
behavior.
|
||||||
|
- Credential configuration contains environment-variable names only. Secret
|
||||||
|
values remain outside the role and managed pipeline document.
|
||||||
|
- The configuration directory is `root:narratio` mode `0750`; the pipeline
|
||||||
|
file is `root:narratio` mode `0640`.
|
||||||
|
- Mutable runtime roots are `narratio:narratio` mode `2775`. The campaign root
|
||||||
|
is `root:narratio` mode `0755` because version-controlled content is
|
||||||
|
populated by a separate owner and is not operator-edited on the host.
|
||||||
|
- Tasks installing dependencies or managing privileged 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
|
||||||
|
|
||||||
|
### Installation and Managed Filesystem
|
||||||
|
|
||||||
|
| Variable | Default | Purpose |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `narratio_version` | `v2.0.0` | Selected Git revision and embedded build version |
|
||||||
|
| `narratio_repository_url` | `https://gitea.maximumdirect.net/eric/narratio.git` | Source repository |
|
||||||
|
| `narratio_source_dir` | `/usr/local/src/narratio` | Managed source checkout |
|
||||||
|
| `narratio_binary_path` | `/usr/local/bin/narratio` | Installed executable |
|
||||||
|
| `narratio_manage_build_dependencies` | `true` | Whether to install Git and Go |
|
||||||
|
| `narratio_go_snap_channel` | `1.26/stable` | Go Snap channel |
|
||||||
|
| `narratio_go_binary` | `/snap/bin/go` | Go executable used for the build |
|
||||||
|
| `narratio_user` | `narratio` | System account owning mutable runtime state |
|
||||||
|
| `narratio_group` | `narratio` | System group sharing mutable runtime state |
|
||||||
|
| `narratio_config_path` | `/usr/local/etc/narratio/pipeline.yml` | Managed root pipeline document |
|
||||||
|
| `narratio_workspace_root` | `/var/lib/narratio` | Managed workspace root and account home |
|
||||||
|
| `narratio_campaigns_root` | `/usr/local/share/narratio/campaigns` | Managed campaign registry root |
|
||||||
|
| `narratio_spool_root` | `/var/spool/narratio` | Managed spool root |
|
||||||
|
| `narratio_cache_root` | `/var/cache/narratio` | Managed cache root |
|
||||||
|
|
||||||
|
Changing a managed root changes both its rendered pipeline value and the
|
||||||
|
directory the role manages. The role does not infer additional arbitrary paths
|
||||||
|
embedded inside caller-supplied open-ended collections.
|
||||||
|
|
||||||
|
### Pipeline Structure and Storage
|
||||||
|
|
||||||
|
Except where a default is shown, every variable below defaults to null and is
|
||||||
|
omitted when null.
|
||||||
|
|
||||||
|
| Variable | Default | Native field |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `narratio_configkit_imports` | `null` | `configkit.imports` |
|
||||||
|
| `narratio_workspace_cleanup_after_publish` | `null` | `workspace.cleanup_after_publish` |
|
||||||
|
| `narratio_campaigns_default_campaign_id` | `null` | `campaigns.default_campaign_id` |
|
||||||
|
| `narratio_storage_backend` | `null` | `storage.backend` |
|
||||||
|
| `narratio_storage_s3_bucket` | `null` | `storage.s3.bucket` |
|
||||||
|
| `narratio_storage_s3_root_prefix` | `null` | `storage.s3.root_prefix` |
|
||||||
|
| `narratio_storage_s3_region` | `null` | `storage.s3.region` |
|
||||||
|
| `narratio_storage_s3_endpoint` | `null` | `storage.s3.endpoint` |
|
||||||
|
| `narratio_storage_s3_force_path_style` | `null` | `storage.s3.force_path_style` |
|
||||||
|
| `narratio_storage_s3_access_key_id` | `null` | `storage.s3.access_key_id` environment-variable name |
|
||||||
|
| `narratio_storage_s3_secret_access_key` | `null` | `storage.s3.secret_access_key` environment-variable name |
|
||||||
|
| `narratio_spool_delete_audio_after_publish` | `null` | `spool.delete_audio_after_publish` |
|
||||||
|
| `narratio_cache_s3_audio` | `null` | `cache.s3_audio` |
|
||||||
|
| `narratio_publish_enabled` | `null` | `publish.enabled` |
|
||||||
|
| `narratio_publish_upload_run` | `null` | `publish.upload_run` |
|
||||||
|
| `narratio_publish_outputs` | `null` | Complete native `publish.outputs` list |
|
||||||
|
|
||||||
|
### WhisperX, Seriatim, and Audita
|
||||||
|
|
||||||
|
| Variable | Default | Native field |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `narratio_whisperx_transcribe_url` | required | `whisperx.transcribe_url` |
|
||||||
|
| `narratio_whisperx_language` | `null` | `whisperx.language` |
|
||||||
|
| `narratio_whisperx_timeout` | `null` | `whisperx.timeout` |
|
||||||
|
| `narratio_whisperx_retries` | `null` | `whisperx.retries` |
|
||||||
|
| `narratio_whisperx_retry_delay` | `null` | `whisperx.retry_delay` |
|
||||||
|
| `narratio_whisperx_concurrency` | `null` | `whisperx.concurrency` |
|
||||||
|
| `narratio_seriatim_binary` | `null` | `seriatim.binary` |
|
||||||
|
| `narratio_seriatim_timeout` | `null` | `seriatim.timeout` |
|
||||||
|
| `narratio_seriatim_output_schema` | `null` | `seriatim.output_schema` |
|
||||||
|
| `narratio_seriatim_coalesce_gap` | `null` | `seriatim.coalesce_gap` |
|
||||||
|
| `narratio_seriatim_report` | `null` | `seriatim.report` |
|
||||||
|
| `narratio_seriatim_env_overlap_word_run_gap` | `null` | `seriatim.env.overlap_word_run_gap` |
|
||||||
|
| `narratio_seriatim_env_overlap_word_run_reorder_window` | `null` | `seriatim.env.overlap_word_run_reorder_window` |
|
||||||
|
| `narratio_seriatim_env_backchannel_max_duration` | `null` | `seriatim.env.backchannel_max_duration` |
|
||||||
|
| `narratio_seriatim_env_filler_max_duration` | `null` | `seriatim.env.filler_max_duration` |
|
||||||
|
| `narratio_audita_binary` | `null` | `audita.binary` |
|
||||||
|
| `narratio_audita_timeout` | `null` | `audita.timeout` |
|
||||||
|
| `narratio_audita_llm_api_key` | `null` | `audita.llm_api_key` environment-variable name |
|
||||||
|
| `narratio_audita_modules` | `null` | `audita.modules` |
|
||||||
|
| `narratio_audita_base_url` | `null` | `audita.base_url` |
|
||||||
|
| `narratio_audita_model` | `null` | `audita.model` |
|
||||||
|
| `narratio_audita_total_llm_concurrency` | `null` | `audita.total_llm_concurrency` |
|
||||||
|
| `narratio_audita_proposal_llm_concurrency` | `null` | `audita.proposal_llm_concurrency` |
|
||||||
|
| `narratio_audita_validation_model` | `null` | `audita.validation_model` |
|
||||||
|
| `narratio_audita_validation_llm_concurrency` | `null` | `audita.validation_llm_concurrency` |
|
||||||
|
| `narratio_audita_transcript_description` | `null` | `audita.transcript_description` |
|
||||||
|
| `narratio_audita_config_path` | `null` | `audita.config_path` |
|
||||||
|
| `narratio_audita_output_schema` | `null` | `audita.output_schema` |
|
||||||
|
| `narratio_audita_work_dir_retention` | `null` | `audita.work_dir_retention` |
|
||||||
|
| `narratio_audita_report` | `null` | `audita.report` |
|
||||||
|
|
||||||
|
### Transcript Stages and Rendering
|
||||||
|
|
||||||
|
| Variable | Default | Native field |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `narratio_normalize_output_path` | `null` | `normalize.output_path` |
|
||||||
|
| `narratio_normalize_output_schema` | `null` | `normalize.output_schema` |
|
||||||
|
| `narratio_normalize_report` | `null` | `normalize.report` |
|
||||||
|
| `narratio_trim_enabled` | `null` | `trim.enabled` |
|
||||||
|
| `narratio_trim_output_path` | `null` | `trim.output_path` |
|
||||||
|
| `narratio_trim_bounds_prompt_id` | `null` | `trim.bounds.prompt_id` |
|
||||||
|
| `narratio_trim_bounds_profile_id` | `null` | `trim.bounds.profile_id` |
|
||||||
|
| `narratio_trim_bounds_transcript_input_name` | `null` | `trim.bounds.transcript_input_name` |
|
||||||
|
| `narratio_trim_bounds_output_path` | `null` | `trim.bounds.output_path` |
|
||||||
|
| `narratio_trim_bounds_timeout` | `null` | `trim.bounds.timeout` |
|
||||||
|
| `narratio_trim_bounds_render_debug` | `null` | `trim.bounds.render_debug` |
|
||||||
|
| `narratio_trim_bounds_render_output_path` | `null` | `trim.bounds.render_output_path` |
|
||||||
|
| `narratio_trim_seriatim_report` | `null` | `trim.seriatim.report` |
|
||||||
|
| `narratio_render_enabled` | `null` | `render.enabled` |
|
||||||
|
| `narratio_render_format` | `null` | `render.format` |
|
||||||
|
| `narratio_render_title` | `null` | `render.title` |
|
||||||
|
| `narratio_render_include_timestamps` | `null` | `render.include_timestamps` |
|
||||||
|
| `narratio_render_include_segment_ids` | `null` | `render.include_segment_ids` |
|
||||||
|
| `narratio_render_include_metadata` | `null` | `render.include_metadata` |
|
||||||
|
|
||||||
|
### Scriptorium, Notarius, and Notification
|
||||||
|
|
||||||
|
| Variable | Default | Native field |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `narratio_scriptorium_binary` | `null` | `scriptorium.binary` |
|
||||||
|
| `narratio_scriptorium_config_path` | `null` | `scriptorium.config_path` |
|
||||||
|
| `narratio_scriptorium_timeout` | `null` | `scriptorium.timeout` |
|
||||||
|
| `narratio_scriptorium_render_debug` | `null` | `scriptorium.render_debug` |
|
||||||
|
| `narratio_scriptorium_artifacts` | `null` | Complete native `scriptorium.artifacts` map |
|
||||||
|
| `narratio_scriptorium_artifact_families` | `null` | Complete native `scriptorium.artifact_families` map |
|
||||||
|
| `narratio_notarius_enabled` | `null` | `notarius.enabled` |
|
||||||
|
| `narratio_notarius_binary` | `null` | `notarius.binary` |
|
||||||
|
| `narratio_notarius_config_path` | `null` | `notarius.config_path` |
|
||||||
|
| `narratio_notarius_pipeline_id` | `null` | `notarius.pipeline_id` |
|
||||||
|
| `narratio_notarius_timeout` | `null` | `notarius.timeout` |
|
||||||
|
| `narratio_notarius_working_directory` | `null` | `notarius.working_directory` |
|
||||||
|
| `narratio_notarius_references` | `null` | Complete native `notarius.references` map |
|
||||||
|
| `narratio_notarius_outputs` | `null` | Complete native `notarius.outputs` map |
|
||||||
|
| `narratio_notification_mode` | `null` | `notification.mode` |
|
||||||
|
|
||||||
|
## Simplest Viable Implementation
|
||||||
|
|
||||||
|
The production task flow is:
|
||||||
|
|
||||||
|
1. Assert the supported platform and architecture.
|
||||||
|
2. Install Git and Go when dependency management is enabled.
|
||||||
|
3. Create the Narratio system group and account.
|
||||||
|
4. Create the source and configuration parent directories.
|
||||||
|
5. Create the mutable runtime roots and read-only campaign registry root.
|
||||||
|
6. Check out the selected Narratio revision with `ansible.builtin.git`.
|
||||||
|
7. Inspect only whether the installed binary exists, because that result is
|
||||||
|
needed to decide whether the imperative build command must run.
|
||||||
|
8. Build directly to the selected binary path when the checkout changed or
|
||||||
|
the binary is absent.
|
||||||
|
9. Converge binary ownership and mode with `ansible.builtin.file`.
|
||||||
|
10. Render the root pipeline document with one
|
||||||
|
`ansible.builtin.template` task.
|
||||||
|
|
||||||
|
The build command uses `narratio_go_binary` and runs only under the two stated
|
||||||
|
conditions. Ordinary modules own all other state checks. No role-private state,
|
||||||
|
pre-build version interrogation, post-build validation, configuration
|
||||||
|
validation, or redundant filesystem inspection belongs in production tasks.
|
||||||
|
|
||||||
|
The template is necessarily larger than those of the downstream roles because
|
||||||
|
Narratio's native pipeline schema is larger. Its logic remains mechanical:
|
||||||
|
serialize the required URL and role-owned roots, test each optional field with
|
||||||
|
`is not none`, emit a parent section only when needed, and pass native
|
||||||
|
collections through unchanged.
|
||||||
|
|
||||||
|
## Rationale and Alternatives
|
||||||
|
|
||||||
|
Narratio's configuration is broad, but a field-specific interface preserves
|
||||||
|
discoverability, argument validation, stable inventory names, and a clean
|
||||||
|
composition boundary for the suite role. A raw complete-document variable
|
||||||
|
would make the template shorter but would discard those benefits and conflict
|
||||||
|
with the established configuration policy. Open-ended native collections are
|
||||||
|
kept whole because decomposing caller-defined keys would create a second schema
|
||||||
|
and make inventory composition harder.
|
||||||
|
|
||||||
|
Requiring the WhisperX URL is preferable to installing a configuration that
|
||||||
|
Narratio cannot validate or inventing an endpoint that might silently direct
|
||||||
|
work to the wrong service. The baseline integration invocation supplies a
|
||||||
|
non-contacted test URL; production callers supply their actual endpoint.
|
||||||
|
|
||||||
|
The standalone role creates the application account and directory permissions
|
||||||
|
because those are Narratio runtime requirements. It does not select human
|
||||||
|
operators, because membership is deployment-wide policy and belongs in the
|
||||||
|
suite role.
|
||||||
|
|
||||||
|
ConfigKit imports and profiles remain available to advanced callers, but the
|
||||||
|
role manages only the canonical root document. This avoids a parallel raw-file
|
||||||
|
deployment subsystem. A caller that selects imports or profiles is responsible
|
||||||
|
for installing those additional files by another role or play.
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
### Required Role Behavior
|
||||||
|
|
||||||
|
- The role rejects unsupported platforms before modifying application state.
|
||||||
|
- On a supported host, it installs the selected Narratio source release and
|
||||||
|
builds the binary with the upstream release contract.
|
||||||
|
- The system account, source, binary, configuration, runtime roots, and
|
||||||
|
campaign root converge to the paths, ownership, and modes selected above
|
||||||
|
through the responsible Ansible modules.
|
||||||
|
- With only `narratio_whisperx_transcribe_url` supplied, the rendered pipeline
|
||||||
|
contains that URL and the four role-owned roots, omits unselected optional
|
||||||
|
policy, and is accepted by Narratio's native configuration validator.
|
||||||
|
- Each non-null configuration variable renders its corresponding native field.
|
||||||
|
Null fields and empty parent sections are omitted, while intentional falsey
|
||||||
|
values are retained.
|
||||||
|
- Caller-supplied open-ended collections are rendered without structural
|
||||||
|
rewriting, and independently defined values 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 `narratio_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-narratio` and the workspace control
|
||||||
|
repository is modified.
|
||||||
|
|
||||||
|
### Required Validation
|
||||||
|
|
||||||
|
- Syntax checking and production-profile ansible-lint pass.
|
||||||
|
- A baseline invocation supplies only the required WhisperX URL, exercises the
|
||||||
|
installed application by validating the managed pipeline as the Narratio
|
||||||
|
account, and is followed immediately by a role application reporting no
|
||||||
|
changes. It does not snapshot the role's current default tag or complete
|
||||||
|
default document.
|
||||||
|
- One explicitly selected-version case requests `v2.0.0` and verifies that
|
||||||
|
`narratio version` reports `v2.0.0`. The assertion proves caller-controlled
|
||||||
|
version selection, not that `v2.0.0` remains the role default.
|
||||||
|
- 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 falsey values, and composed
|
||||||
|
open-ended collections. Narratio's native `config validate` accepts the
|
||||||
|
managed pipeline, 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 account, directory, file, package, checkout, or template
|
||||||
|
postconditions, or an exhaustive negative platform matrix.
|
||||||
|
|
||||||
|
## Dependencies and Follow-up Work
|
||||||
|
|
||||||
|
The managed host requires access to the Narratio Git repository, Ubuntu Snap
|
||||||
|
infrastructure, and Go module sources. Useful pipeline execution also requires
|
||||||
|
a reachable WhisperX service, the four downstream application binaries and
|
||||||
|
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
|
||||||
|
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.
|
||||||
|
|
||||||
|
## 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 imported fragments or profile overlays if a concrete standalone-role
|
||||||
|
use case requires them.
|
||||||
|
- Add a scheduler, service, or non-interactive execution environment if later
|
||||||
|
operating requirements select one.
|
||||||
|
- Support Narratio's published binary artifacts as part of a future suite-wide
|
||||||
|
installation-model change.
|
||||||
|
- Add broader platform coverage only when supported-platform behavior expands.
|
||||||
@@ -1,14 +1,413 @@
|
|||||||
# Implementation Plan
|
# Implementation Plan 0005: Install and Configure Narratio
|
||||||
|
|
||||||
Status: pending
|
Status: ready
|
||||||
|
|
||||||
No implementation plan is currently active. Feature 0004 was implemented in
|
Target executor: GPT-5.6-Terra
|
||||||
`ansible-role-scriptorium` commits `90d7f45` and `5944eac`, independently
|
|
||||||
reviewed, and finalized by remediation commit `8f47aa8`.
|
|
||||||
|
|
||||||
Its durable scope and rationale remain in
|
Feature roadmap:
|
||||||
[the Feature 0004 roadmap](features/0004-scriptorium-source-installation-and-configuration.md),
|
[Feature 0005: Narratio Source Installation and Configuration](features/0005-narratio-source-installation-and-configuration.md)
|
||||||
and its final review record remains in [the remediation record](remediation.md).
|
|
||||||
|
|
||||||
Select and approve the next unit of work before replacing this notice with a
|
Active plan location: `docs/roadmap/implementation.md`
|
||||||
new staged implementation plan.
|
|
||||||
|
## 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
|
||||||
|
|
||||||
|
- No stages completed.
|
||||||
|
|||||||
@@ -176,18 +176,48 @@ application-role releases in one source repository.
|
|||||||
|
|
||||||
### Accounts, ownership, and operator access
|
### Accounts, ownership, and operator access
|
||||||
|
|
||||||
- The roles create a dedicated `narratio` user and `narratio` group as needed.
|
- The standalone Narratio role creates a dedicated system `narratio` user and
|
||||||
|
`narratio` group. The account uses `/var/lib/narratio` as its default home
|
||||||
|
and has a non-login shell.
|
||||||
- The `narratio` account owns Narratio's writable runtime directories.
|
- The `narratio` account owns Narratio's writable runtime directories.
|
||||||
- Application source trees and installed binaries are owned by `root` and are
|
- Application source trees and installed binaries are owned by `root` and are
|
||||||
not writable by operators or the runtime account.
|
not writable by operators or the runtime account.
|
||||||
- Ordinary human users run `narratio run` and other Narratio commands directly.
|
- Ordinary human users run `narratio run` and other Narratio commands directly.
|
||||||
- Those operators receive the required access by membership in the `narratio`
|
- Those operators receive the required access by membership in the `narratio`
|
||||||
group.
|
group.
|
||||||
- Managed configuration should be owned by `root`, readable by the `narratio`
|
- Narratio's configuration directory is owned by `root:narratio` with mode
|
||||||
group where needed, and remain under Ansible control rather than being
|
`0750`, and its pipeline file is owned by `root:narratio` with mode `0640`.
|
||||||
routinely edited by operators on the host.
|
Configuration remains under Ansible control rather than being routinely
|
||||||
- Shared runtime directories must grant the `narratio` group the write access
|
edited by operators on the host.
|
||||||
needed for direct operator invocations.
|
- The Narratio workspace, spool, and cache roots are owned by
|
||||||
|
`narratio:narratio` with mode `2775`, preserving the group on created content
|
||||||
|
and granting the shared write access needed for direct operator invocations.
|
||||||
|
- The campaign registry root is owned by `root:narratio` with mode `0755`.
|
||||||
|
Its content is readable by operators but is populated by the suite role and
|
||||||
|
is not edited directly on the managed host.
|
||||||
|
- The standalone role does not select human operators. The suite role owns the
|
||||||
|
operator list and adds those accounts to the `narratio` group.
|
||||||
|
|
||||||
|
### Narratio configuration
|
||||||
|
|
||||||
|
- The standalone Narratio role manages the canonical root pipeline document
|
||||||
|
at `/usr/local/etc/narratio/pipeline.yml` through a field-specific public
|
||||||
|
variable interface.
|
||||||
|
- The role requires a caller-supplied WhisperX transcription URL because the
|
||||||
|
field is required by Narratio and has no valid universal deployment value.
|
||||||
|
- Filesystem roots managed by the role have concrete defaults and are rendered
|
||||||
|
so application configuration agrees with managed host state. Other optional
|
||||||
|
application fields default to null, are omitted from the rendered document,
|
||||||
|
and inherit Narratio's built-in defaults.
|
||||||
|
- Native open-ended collections such as publish outputs, Notarius references
|
||||||
|
and outputs, and Scriptorium artifacts and artifact families are exposed as
|
||||||
|
complete fields. Callers may compose their final values in inventory; the
|
||||||
|
role does not add merge or precedence behavior.
|
||||||
|
- The root document may name ConfigKit imports, but the standalone role does
|
||||||
|
not manage imported fragments or profile overlays. A caller that selects
|
||||||
|
them owns their deployment.
|
||||||
|
- Credential fields contain environment-variable names, never secret values.
|
||||||
|
Secret delivery remains suite-level policy.
|
||||||
|
|
||||||
### Execution model
|
### Execution model
|
||||||
|
|
||||||
@@ -237,16 +267,12 @@ remain to be designed.
|
|||||||
|
|
||||||
The following areas require investigation and decisions before implementation:
|
The following areas require investigation and decisions before implementation:
|
||||||
|
|
||||||
- Exact application repository URLs, default release tags, build commands,
|
|
||||||
version-injection flags, and validation commands
|
|
||||||
- Standalone role Galaxy names and the dependency-manifest/bootstrap layout
|
- Standalone role Galaxy names and the dependency-manifest/bootstrap layout
|
||||||
- The mechanism used to incorporate the independently developed pipeline role
|
- The mechanism used to incorporate the independently developed pipeline role
|
||||||
into collection releases
|
into collection releases
|
||||||
- Narratio's application configuration contract and runtime filesystem
|
|
||||||
requirements
|
|
||||||
- The prompt-library destination and its integration with Scriptorium
|
- The prompt-library destination and its integration with Scriptorium
|
||||||
- Secret injection and storage
|
- Secret injection and storage
|
||||||
- Operator-group membership variables and directory permission details
|
- Suite-level operator-selection variables
|
||||||
- Upgrade, downgrade, and removal behavior
|
- Upgrade, downgrade, and removal behavior
|
||||||
- Collection bootstrap and end-to-end validation details beyond the locked
|
- Collection bootstrap and end-to-end validation details beyond the locked
|
||||||
dependency and testing policies above
|
dependency and testing policies above
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Last updated: 2026-09-09
|
|||||||
|
|
||||||
Current milestone: MVP standalone application roles
|
Current milestone: MVP standalone application roles
|
||||||
|
|
||||||
Current focus: Select the next unit of work.
|
Current focus: Implement Feature 0005 for the standalone Narratio role.
|
||||||
|
|
||||||
| ID | Deliverable | Owner | State | Evidence or next step |
|
| ID | Deliverable | Owner | State | Evidence or next step |
|
||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
@@ -14,13 +14,14 @@ Current focus: Select the next unit of work.
|
|||||||
| M0-04 | Aggregate repository status | workspace | done | `scripts/status` created |
|
| 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` |
|
| 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-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 Narratio standalone-role feature |
|
| M1-02 | Standalone application roles | role repositories | in_progress | Implement and review Feature 0005 |
|
||||||
| M1-03 | Suite-level pipeline role | ansible-role-dnd-pipeline | proposed | Begin after standalone role interfaces stabilize |
|
| 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 |
|
| 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` |
|
| 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 |
|
| 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` |
|
| 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` |
|
| 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` |
|
||||||
| A0001 | Audit completed standalone-role test suites | workspace and standalone roles | done | Findings recorded in `docs/roadmap/test-suite-audit-findings.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 |
|
| R0001 | Simplify completed standalone-role test suites | workspace and standalone roles | done | Role commits `e08c63c`, `d2d0b9d`, and `cfecf37` passed implementation and independent review |
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user