Files
ansible-workspace-dnd/docs/roadmap/features/0005-narratio-source-installation-and-configuration.md

26 KiB

Feature 0005: Narratio Source Installation and Configuration

Status: done

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 governs the role implementation, public configuration interface, and template.
  • The testing house style 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 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.

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.
  • 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.