# Configuration Reference ## Purpose Narratio resolves three YAML documents: - `pipeline.yml`: pipeline/runtime settings - `campaign.yml`: campaign identity and stable input defaults - `session.yml`: session identity, metadata, and audio source selection ## Discovery and Selection ### `pipeline.yml` When `--config` is omitted, search order is: 1. `/usr/local/etc/narratio/pipeline.yml` 2. `/etc/narratio/pipeline.yml` ### `campaign.yml` Selection rules: - if `--campaign-file` is set, use that path; - else if `--campaign ` is set, use `{pipeline.campaigns.root}/{id}/campaign.yml`; - else use `{pipeline.campaigns.root}/{pipeline.campaigns.default_campaign_id}/campaign.yml`. ### `session.yml` When `--session` is omitted, local search order is: 1. `/usr/local/etc/narratio/session.yml` 2. `/etc/narratio/session.yml` If local session discovery fails and a `session_id` is known, Narratio attempts remote session loading from: - `{root_prefix}/campaigns/{campaign}/sessions/{session_id}/session.yml` using configured object storage. The downloaded remote session file is command-scoped: Narratio removes it after the command finishes and records only the remote object provenance alongside the durable copied session input. ### Read-only effective pipeline inspection `narratio config validate`, `narratio config show`, and `narratio config sources` use the same `--config`, `--campaign`, `--campaign-file`, and `--profile` selection rules as pipeline commands, but do not select, discover, or load a session. They do not read credential values or create runtime state. `narratio config diff ` uses the same pipeline and campaign selectors, resolves each named profile with an independent ConfigKit load, and does not accept a separate `--profile` flag. Campaign selection is optional only when the resolved pipeline has no `scriptorium.artifact_families`. When families are declared, Narratio selects a campaign through an explicit flag or `pipeline.campaigns.default_campaign_id`, then parses the campaign-owned party and expands concrete artifacts and any family publish rules before validation. `config validate` prints the resulting root, profile, and effective digest. `config show` emits the normalized effective pipeline YAML, with defaults and concrete expansion included but ConfigKit directives and family declarations omitted. `config sources` prints a stable source projection instead of effective values: root/import/profile/default ownership plus campaign/party and generated-family records. Canonical derived players trace to the party. The [CLI reference](cli.md#config-validate-config-show-config-sources-and-config-diff) owns command syntax and output conventions. `config diff` compares normalized field values rather than YAML text or source ownership. It emits sorted `added`, `removed`, and `changed` records, uses compact deterministic JSON values, treats lists atomically, and reports `no differences` when the complete effective configurations are equal. Concrete family members and generated publish rules participate after expansion; moving an equal value between eligible root/import sources does not create a difference. ### Migrating to the maintained bundle Use the [production/testing bundle](../examples/production-testing/pipeline.yml) as the complete copyable migration reference. Split stable pipeline settings into explicit additive imports and place production/testing differences in the fixed `profiles/production.yml` and `profiles/testing.yml` overlays. Convert campaign rosters to [canonical party input](integrations/party.md), then express character work as families. Inspect the result with `config validate`, `config show`, and `config sources`; use `config diff` to review profiles before running a session. Unversioned party documents are rejected. ### Identity segments Campaign IDs (`campaign_id` and `default_campaign_id`), session IDs, previous session IDs, and Narratio run IDs are opaque portable segments. They must use only ASCII letters, digits, `.`, `_`, and `-`; empty values, `.`/`..`, path separators, drive forms, whitespace, control characters, and non-ASCII text are rejected. Narratio does not trim or rewrite these values. Existing manifests or remote state with an unsafe legacy identity must be migrated before use. ## Validation and Merge Rules - ConfigKit performs strict typed YAML decoding and accepts exactly one document; unknown fields, duplicate keys, and trailing documents fail load. - A pipeline file may explicitly import additive YAML fragments through the root-only `configkit.imports` list. Imported files contribute fields to one logical pipeline document; they do not override fields supplied by the root or another import. - An omitted profile, an empty profile option, and the exact name `default` select the base configuration. Any other name selects `profiles/.yml` relative to the pipeline root. - Campaign, session, and canonical-party documents are strict single-source ConfigKit schemas. They do not enable imports, profiles, or `configkit` directives. - Configured timeout and retry-delay durations must be positive. An omitted artifact timeout continues to inherit its configured Scriptorium timeout. - Session files must be concrete; unresolved `{{ ... }}` placeholders fail load. - Session and party inputs inspected before typed loading are limited to 8 MiB. - Pipeline defaults are applied before validation. - Campaign and session identities must agree. - Required stable files (`speakers_file`, `autocorrect_file`, `glossary_file`, `party_file`) are campaign-owned; the first three may be overridden by the session. The optional `spell_catalog_file` also inherits a campaign value when omitted by the session. - `party_file` is parsed when pipeline and campaign configuration are combined. It must be a versioned [canonical party](integrations/party.md), is campaign-owned and derives the players input internally. `players_file` and session `party_file` are unsupported configuration keys. - Exactly one audio mode must be configured in session input: - local (`audio_dir` or `audio_files`), or - S3 (`audio_s3.prefix`). ### Pipeline imports and profiles Large pipeline configurations may be split into explicitly named fragments and may use fixed-path profile overlays: ```yaml configkit: imports: - path: config/storage.yml - path: config/integrations.yaml campaigns: root: /usr/local/share/narratio/campaigns ``` The maintained [production/testing bundle](../examples/production-testing/pipeline.yml) is a complete copyable example of this structure, including canonical-party artifact families. Imports are resolved relative to the directory containing the root pipeline file and are loaded in declaration order. Narratio does not scan directories or infer fragments. Each import must be a confined regular `.yml` or `.yaml` file: absolute paths, traversal, symlinks, directories, duplicate files, and an import of the root pipeline itself are rejected. Imported files cannot declare another `configkit` control section. Base merging is additive. A map may be extended by multiple files when every leaf is distinct, but a scalar, list, or map/list/scalar kind cannot be claimed more than once, even when the repeated values are identical. Conflict errors name the full field path and every source that claimed it. Narratio additionally rejects an artifact or artifact-family key declared by more than one base file, even when those files contribute disjoint children. Profile names are case-sensitive and use ConfigKit's safe fixed-path naming rules. Only the selected overlay is opened; a malformed unselected overlay does not affect a base or different-profile load. Maps merge recursively, overlay scalars replace base scalars, and overlay lists replace base lists completely. Explicit `false`, zero, empty strings, empty containers, and null values remain explicit inputs and are not refilled by defaults. Profiles cannot inherit, stack, import files, or declare another ConfigKit control section. During ConfigKit loading, Narratio applies fresh typed defaults, recorded entry-level defaults and normalization, read-only file-local validation, and ordinary path resolution before computing a deterministic effective configuration digest. The digest represents the normalized, secret-free runtime pipeline mapping; it excludes ConfigKit directives, source provenance, profile identity, and raw environment secret values. Equivalent effective mappings therefore have the same digest regardless of how fields are split among the root and imports. An imported field has the same meaning it would have in a monolithic root pipeline. In particular, ordinary relative pipeline paths continue to resolve from the root pipeline directory, not from the importing fragment's directory. ## Minimal Working Configuration `pipeline.yml` ```yaml campaigns: root: /usr/local/share/narratio/campaigns default_campaign_id: sample-campaign whisperx: transcribe_url: https://transcription.example.com/transcribe ``` `campaign.yml` ```yaml campaign_id: sample-campaign inputs: speakers_file: ./speakers.yml autocorrect_file: ./autocorrect.yml glossary_file: ./glossary.yml party_file: ./party.yml ``` `session.yml` (local audio) ```yaml session_id: 2026-05-03 inputs: audio_dir: ./audio ``` ## Secrets Handling - Do not place raw secrets in YAML. - Use env var names in config (for example `pipeline.audita.llm_api_key_env`). - Every command that loads a pipeline accepts `--secrets-dir `. When the flag is omitted, Narratio uses a present `secrets/` directory beside the resolved root `pipeline.yml`; an absent conventional directory is ignored. Explicit relative paths are resolved from the caller's working directory, and a missing or empty explicit selection is an error. - Each immediate regular file whose name is a valid environment-variable name supplies one value. ConfigKit removes trailing CR/LF, permits a symlink as the selected directory, rejects symlink entries, and limits input to 1 MiB per file, 8 MiB total, and 1024 entries. - An existing process environment value takes precedence over a credential file. - On POSIX, operators must provision the directory with no group/other access (normally `0700`) and files with no group/other access (normally `0600`); ConfigKit validates type and bounds but does not enforce permission modes. - Bootstrap happens once per command, before any configuration load or adapter construction. Safe reports contain filenames, paths, and statuses, never values. ## Publish Configuration Summary Publish rules live under `pipeline.publish`. ```yaml publish: enabled: true upload_run: true outputs: - source: narratio.transcript.final_trimmed dest: transcripts/final.trimmed.json required: true - source: narratio.transcript.final_markdown dest: transcripts/final.md required: true - source: narratio.transcript.final_trimmed_markdown dest: transcripts/final.trimmed.md required: true - source: narratio.artifact.session_recap dest: artifacts/session_recap.md required: true ``` Rules: - `outputs[].source` is required. - `outputs[].dest` may be omitted when derivable from source. - extraction sources require an explicit `outputs[].dest` and publish only when a rule names that source; the Notarius index and complete bundle are not publish sources. - `outputs[].required` defaults to `true`. - publish locks are independent remote session objects managed with `narratio session locks`; they are not pipeline configuration fields. ## Full Schema ### Pipeline | Field | Type | Required | Default / Rule | | --- | --- | --- | --- | | `configkit.imports[].path` | string | No | explicit additive pipeline fragment relative to the root pipeline directory; `.yml` or `.yaml` regular files only | | `pipeline.workspace.root` | string | No | `/var/lib/narratio` | | `pipeline.workspace.cleanup_after_publish` | bool | No | `false`; selects the published run's workspace target for final cleanup and requires effective `pipeline.publish.enabled=true` and `pipeline.publish.upload_run=true` | | `pipeline.campaigns.root` | string | No | `/usr/local/share/narratio/campaigns` | | `pipeline.campaigns.default_campaign_id` | string | No | empty | | `pipeline.storage.backend` | string | No | `local`; supported values are `local` and `s3` (case-insensitive) | | `pipeline.storage.s3.bucket` | string | Conditional | required when backend is `s3` and S3 session-audio or publish upload is enabled | | `pipeline.storage.s3.root_prefix` | string | No | `dnd` | | `pipeline.storage.s3.region` | string | No | empty | | `pipeline.storage.s3.endpoint` | string | No | empty | | `pipeline.storage.s3.force_path_style` | bool | No | `false` | | `pipeline.storage.s3.access_key_id_env` | string | No | `OBJECT_STORAGE_KEY_ID` | | `pipeline.storage.s3.secret_access_key_env` | string | No | `OBJECT_STORAGE_KEY` | | `pipeline.spool.root` | string | No | `/var/spool/narratio` | | `pipeline.spool.delete_audio_after_publish` | bool | No | `false`; selects the published run's spool-audio target for final cleanup and requires effective `pipeline.publish.enabled=true` and `pipeline.publish.upload_run=true` | | `pipeline.cache.root` | string | No | `/var/cache/narratio` | | `pipeline.cache.s3_audio` | bool | No | `true` | | `pipeline.publish.enabled` | bool | No | `true` | | `pipeline.publish.upload_run` | bool | No | `true` | | `pipeline.publish.outputs[]` | list | No | defaults to final trimmed JSON plus final and final-trimmed Markdown outputs | | `pipeline.publish.outputs[].source` | string | Yes (per rule) | must reference built-in or configured artifact source | | `pipeline.publish.outputs[].dest` | string | Conditional | derived if omitted and source supports derivation | | `pipeline.publish.outputs[].required` | bool | No | `true` | | `pipeline.whisperx.transcribe_url` | string | Yes | absolute `http` or `https` URL | | `pipeline.whisperx.language` | string | No | `en` | | `pipeline.whisperx.timeout` | duration | No | `30m` | | `pipeline.whisperx.retries` | int | No | `3` | | `pipeline.whisperx.retry_delay` | duration | No | `2s` | | `pipeline.whisperx.concurrency` | int | No | `2` | | `pipeline.seriatim.binary` | string | No | `seriatim` | | `pipeline.seriatim.timeout` | duration | No | `10m` | | `pipeline.seriatim.output_schema` | string | No | `seriatim-intermediate` | | `pipeline.seriatim.coalesce_gap` | float | No | `3.0` | | `pipeline.seriatim.report` | bool | No | `true` | | `pipeline.seriatim.env.overlap_word_run_gap` | float | No | unset | | `pipeline.seriatim.env.overlap_word_run_reorder_window` | float | No | unset | | `pipeline.seriatim.env.backchannel_max_duration` | float | No | unset | | `pipeline.seriatim.env.filler_max_duration` | float | No | unset | | `pipeline.audita.binary` | string | No | `audita` | | `pipeline.audita.timeout` | duration | No | `3h` | | `pipeline.audita.llm_api_key_env` | string | No | empty | | `pipeline.audita.modules[]` | list[string] | No | empty | | `pipeline.audita.base_url` | string | No | empty | | `pipeline.audita.model` | string | No | empty | | `pipeline.audita.total_llm_concurrency` | int | No | unset | | `pipeline.audita.proposal_llm_concurrency` | int | No | unset | | `pipeline.audita.validation_model` | string | No | empty | | `pipeline.audita.validation_llm_concurrency` | int | No | unset | | `pipeline.audita.transcript_description` | string | No | empty | | `pipeline.audita.config_path` | string | No | empty | | `pipeline.audita.output_schema` | string | No | empty | | `pipeline.audita.work_dir_retention` | string | No | empty | | `pipeline.audita.report` | bool | No | `true` | | `pipeline.normalize.output_path` | string | No | `transcripts/final.json` | | `pipeline.normalize.output_schema` | string | No | `seriatim-intermediate` | | `pipeline.normalize.report` | bool | No | `true` | | `pipeline.trim.enabled` | bool | No | `true` | | `pipeline.trim.output_path` | string | No | `transcripts/final.trimmed.json` | | `pipeline.trim.bounds.prompt_id` | string | No | `dnd.session_bounds` | | `pipeline.trim.bounds.profile_id` | string | No | empty | | `pipeline.trim.bounds.transcript_input_name` | string | No | `transcript` | | `pipeline.trim.bounds.output_path` | string | No | `artifacts/session_bounds.json` | | `pipeline.trim.bounds.timeout` | duration | No | `10m` | | `pipeline.trim.bounds.render_debug` | bool | No | `false` | | `pipeline.trim.bounds.render_output_path` | string | Conditional | required when `render_debug` is true | | `pipeline.trim.seriatim.report` | bool | No | `false` | | `pipeline.notarius.enabled` | bool | No | `false` | | `pipeline.notarius.binary` | string | No | `notarius` | | `pipeline.notarius.config_path` | string | Conditional | required when enabled; relative paths resolve from the pipeline file directory | | `pipeline.notarius.pipeline_id` | string | Conditional | required when enabled | | `pipeline.notarius.timeout` | duration | No | `3h`; must be positive | | `pipeline.notarius.working_directory` | string | No | directory containing resolved `config_path`; relative paths resolve from the pipeline file directory | | `pipeline.notarius.references` | map[string]string | No | empty; maps normalized Notarius selectors to supported prepared Narratio source IDs; maximum 256 entries | | `pipeline.notarius.outputs` | map | Conditional | at least one entry when enabled | | `pipeline.render.enabled` | bool | No | `true` | | `pipeline.render.format` | string | No | `markdown` (only supported value) | | `pipeline.render.title` | string | No | empty (falls back to `session.title` when set) | | `pipeline.render.include_timestamps` | bool | No | `true` | | `pipeline.render.include_segment_ids` | bool | No | `true` | | `pipeline.render.include_metadata` | bool | No | `false` | | `pipeline.scriptorium.binary` | string | No | `scriptorium` | | `pipeline.scriptorium.config_path` | string | No | empty | | `pipeline.scriptorium.timeout` | duration | No | `10m` | | `pipeline.scriptorium.render_debug` | bool | No | `false` | | `pipeline.scriptorium.artifacts` | map | No | empty | | `pipeline.scriptorium.artifact_families` | map | No | empty; expands one ordinary artifact per canonical party character | | `pipeline.notification.mode` | string | No | `noop`; the only supported notification mode until a provider is implemented | ### Notarius Reference Bindings `pipeline.notarius.references` maps a Notarius CLI selector to a prepared Narratio source, not to a filesystem path: ```yaml notarius: references: glossary: narratio.input.glossary party: narratio.input.party players: narratio.input.players spell_catalog: narratio.input.spell_catalog ``` Supported sources are `narratio.input.party`, `narratio.input.players`, `narratio.input.glossary`, and `narratio.input.spell_catalog`. Each map entry is required by its presence: omit a binding when the selected Notarius pipeline does not need it. A spell-catalog binding additionally requires an effective campaign or session `spell_catalog_file`. Selectors accept Notarius's `slot`, `chunk.slot`, `lane.slot`, `lane.extract.slot`, `lane.merge.slot`, and `lane.normalize.slot` forms. Narratio trims whitespace around selectors and their dot-separated components, rejects empty components and `=`, rejects duplicate normalized selectors, and limits the map to 256 entries. It validates only selector structure and the prepared source vocabulary; Notarius owns target-slot declarations and media compatibility. Before extraction, Narratio resolves every binding from the current prepared session manifest and streams it into a verified invocation-local snapshot whose absolute path is passed to Notarius. Missing, unsafe, empty, changed-during-copy, or checksum-inconsistent prepared evidence fails with guidance to force `prepare`. Bindings are sorted by normalized selector and are part of extraction fingerprint and resume identity. See the [Notarius integration contract](./integrations/notarius.md) for the subprocess boundary and the [complete example](../examples/pipeline.full.annotated.yml) for a copyable configuration. ### Notarius Output Entries For each `pipeline.notarius.outputs.`: | Field | Type | Required | Rule | | --- | --- | --- | --- | | `lane_id` | string | Yes | unique Notarius lane ID | | `media_type` | string | Yes | exact accepted descriptor media type | | `schema_id` | string | Yes | exact accepted descriptor schema ID | | `schema_version` | string | Yes | exact accepted descriptor schema version | | `module_key` | string | No | exact accepted module key when set | Output names must match `^[a-z][a-z0-9_]*$` and become selectable sources named `narratio.extraction.`. Lane IDs must be unique. Every declared output is required from a successful Notarius result; a missing, rejected, duplicate, or contract-incompatible lane fails extraction. See the [complete maintained example](../examples/pipeline.full.annotated.yml) for the current ten-lane D&D mapping and the [Notarius contract](./integrations/notarius.md) for compatibility ownership. ### Scriptorium Artifact Entries For each `pipeline.scriptorium.artifacts.`: | Field | Type | Required | Rule | | --- | --- | --- | --- | | `enabled` | bool | No | `false` if omitted | | `depends_on[]` | list[string] | No | must reference configured artifact keys; no self-reference; configured graph must be acyclic | | `render_debug` | bool | No | per-artifact override | | `prompt_id` | string | Conditional | required when artifact is enabled | | `profile_id` | string | No | empty | | `output_path` | string | Conditional | required when enabled; also required when referenced by publish/output/input rules | | `timeout` | duration | No | artifact override | | `inputs` | map | No | input key names must be non-empty | | `vars` | map | No | values must be string or bool; `session_id` is reserved and overwritten by Narratio | Narratio adds `session_id=narratio-session-` to every Scriptorium request for sticky upstream LLM routing. If an artifact config sets `vars.session_id`, Narratio replaces that value before invoking Scriptorium. Use a different variable name if a prompt needs the raw Narratio session ID as content. Without `--artifacts`, analyze executes enabled configured artifacts. With an explicit `--artifacts` list, the exact named configured artifacts are the one-invocation targets even if their `enabled` values are false. Analyze closes those targets over `depends_on`: a current prerequisite is reused, while a stale, missing, or failed prerequisite is rebuilt before its dependent. Unrelated artifacts are not executed. Named targets and any prerequisite that may require rebuilding must therefore have valid executable fields. This override affects analyze planning only; publish uses the list only to filter configured `narratio.artifact.` output rules. For each artifact input `pipeline.scriptorium.artifacts..inputs.`: | Field | Type | Required | Rule | | --- | --- | --- | --- | | `source` | string | Yes | built-in runtime source, prepared input source, `narratio.extraction.`, `narratio.artifact.`, or `narratio.previous_session.artifact.` | | `required` | bool | No | optional input requirement | `artifact` and `path` are obsolete and rejected by strict configuration loading. Use the canonical `source` identifier to select the input; Narratio does not provide adapter-specific input passthrough fields. ### Scriptorium Artifact Families `pipeline.scriptorium.artifact_families` declares a shared artifact template for every canonical campaign character. Configuration resolution expands each family into ordinary `pipeline.scriptorium.artifacts` entries before analyze planning or Scriptorium invocation. Party parsing rejects unsupported document schemas before family expansion. For each `pipeline.scriptorium.artifact_families.`: | Field | Type | Required | Rule | | --- | --- | --- | --- | | `enabled`, `prompt_id`, `profile_id`, `timeout`, `render_debug`, `depends_on`, `inputs`, `vars` | ordinary artifact fields | No | copied to each generated artifact under the corresponding ordinary rules | | `for_each` | string | Yes | exactly `party.characters` | | `output_path_pattern` | string | Yes | safe path beneath `artifacts/` with exactly one `{character_id}` token and no other brace syntax | | `member_vars` | map | No | maps an ordinary Scriptorium variable name to a supported canonical character selector | | `member_dependencies` | list | No | unique family keys; each generated member depends on the corresponding generated member of each listed family | | `publish` | map | No | typed family publish policy (`enabled`, `required`, `dest_pattern`) expanded into concrete publish outputs when enabled | Generated keys are `_` and generated output paths must not collide with explicit artifacts or another generated artifact. Families expand even when disabled; normal analyze selection still omits disabled artifacts unless they are explicitly selected by their concrete key. Supported `member_vars` selectors are `character_id`, `player.name`, `character.name`, `character.class_summary`, and `character.alias_summary`. Their resolved values are strings. A member variable may not reuse a static `vars` name; `session_id` remains owned and overwritten by Narratio as for any other Scriptorium artifact. Within a family only, an input source may use `narratio.member_artifact.`. The referenced family must be named in that family's `member_dependencies`; resolution rewrites the source to the corresponding ordinary `narratio.artifact._` source. This syntax is rejected in explicit artifacts and never reaches runtime stages or Scriptorium. ### Notifications Narratio currently supports only `notification.mode: noop`, which is also the default when the section is omitted. The notify stage performs no delivery in this mode. Backend, recipient, timeout, and other provider settings are rejected by strict configuration loading until Narratio has a provider integration. ### Campaign | Field | Type | Required | Notes | | --- | --- | --- | --- | | `campaign_id` | string | Yes | canonical opaque campaign identity | | `session_template_file` | string | No | used by `session init` when set | | `inputs.speakers_file` | string | Yes | stable input default | | `inputs.autocorrect_file` | string | Yes | stable input default | | `inputs.glossary_file` | string | Yes | stable input default | | `inputs.party_file` | string | Yes | stable campaign party source; relative paths resolve from `campaign.yml` | | `inputs.spell_catalog_file` | string | No | optional spell-catalog overlay default; required when a Notarius reference selects `narratio.input.spell_catalog` | ### Session | Field | Type | Required in session file | Notes | | --- | --- | --- | --- | | `session_id` | string | Yes | opaque identity; must match CLI session target when provided | | `previous_session_id` | string | No | opaque identity; must not equal `session_id` | | `campaign` | string | No | opaque identity; filled from `campaign_id` during resolve if omitted | | `date` | string | No | metadata | | `title` | string | No | metadata | | `inputs.speakers_file` | string | No | overrides campaign stable input | | `inputs.autocorrect_file` | string | No | overrides campaign stable input | | `inputs.glossary_file` | string | No | overrides campaign stable input | | `inputs.spell_catalog_file` | string | No | overrides the optional campaign spell catalog; empty or omitted inherits the campaign value | | `inputs.audio_dir` | string | Conditional | local audio mode | | `inputs.audio_files[]` | list[string] | Conditional | local audio mode | | `inputs.audio_s3.prefix` | string | Conditional | S3 audio mode | Audio rules: - configure local mode (`audio_dir` or `audio_files`) or S3 mode (`audio_s3.prefix`), not both. - `audio_s3` requires `pipeline.storage.backend: s3` and a configured S3 bucket. ### Storage backend selection `local` is the default and disables remote object-store operations. Configure `s3` explicitly before supplying `storage.s3`; a populated S3 block does not select a backend on its own. Unknown backend names and an S3 block paired with `local` are rejected during configuration validation. ### Previous-session expectation `previous_session_id` is optional in a session file. When a command supplies `--previous-session-id`, however, the session file must contain the same value; an omitted or different value is rejected before the command performs work. ## Maintained Examples See the [maintained examples index](../examples/README.md) for complete pipeline, campaign, session, template, and input fixtures. Keep complete copyable files there rather than duplicating them in this reference.