Resolve canonical parties with campaign configuration

This commit is contained in:
2026-08-30 14:01:51 +00:00
parent 7e4ceb3d48
commit 61000a9466
18 changed files with 607 additions and 45 deletions

View File

@@ -69,9 +69,15 @@ remote state with an unsafe legacy identity must be migrated before use.
- Pipeline defaults are applied before validation.
- Campaign and session identities must agree.
- Required stable files (`speakers_file`, `autocorrect_file`, `glossary_file`,
`players_file`, `party_file`) and the optional `spell_catalog_file` resolve
from session overrides when provided, otherwise from campaign defaults. An
empty or omitted session spell-catalog value inherits the campaign value.
`party_file`) and the optional `spell_catalog_file` resolve from session
overrides when provided, otherwise from campaign defaults. An empty or
omitted session spell-catalog value inherits the campaign value.
- `party_file` is classified when pipeline and campaign configuration are
combined. A versioned [canonical party](integrations/party.md) is
campaign-owned, derives the players input internally, and forbids both a
separate `players_file` and a session `party_file` override. An unversioned
party remains a bounded legacy input and requires `players_file`; its normal
campaign/session overrides continue to apply.
- Exactly one audio mode must be configured in session input:
- local (`audio_dir` or `audio_files`), or
- S3 (`audio_s3.prefix`).
@@ -433,8 +439,8 @@ integration.
| `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.players_file` | string | Yes | stable input default |
| `inputs.party_file` | string | Yes | stable input default |
| `inputs.players_file` | string | Conditional | required only with an unversioned legacy `party_file`; forbidden for a canonical party |
| `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
@@ -449,8 +455,8 @@ integration.
| `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.players_file` | string | No | overrides campaign stable input |
| `inputs.party_file` | string | No | overrides campaign stable input |
| `inputs.players_file` | string | No | legacy-party override; forbidden for a canonical party |
| `inputs.party_file` | string | No | legacy-party override; forbidden for a canonical campaign party |
| `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 |

View File

@@ -21,6 +21,7 @@ focused stage documents.
- [Audita](./audita.md): transcript polishing (`audita process`).
- [Notarius](./notarius.md): complete pipeline execution and safe JSON bundle
discovery (`notarius run`).
- [Party](./party.md): canonical campaign roster input.
- [Seriatim](./seriatim.md): merge, normalize, trim, and render operations.
- [Scriptorium](./scriptorium.md): artifact generation and debug rendering
(`scriptorium run|render`).

View File

@@ -0,0 +1,49 @@
# Canonical Party Input
`party.yml` is a campaign-owned roster input. Narratio recognizes the
versioned `narratio.party.v1` document below when it resolves a pipeline,
campaign, and session together.
```yaml
schema_version: narratio.party.v1
characters:
arannis:
player:
name: Eric
character:
name: Arannis
alias:
- Ari
- The Grey Owl
classes:
- name: wizard
level: 8
```
`characters` is a non-empty mapping. Each key is a stable character ID using
the configured-artifact key grammar: a lowercase ASCII letter followed by zero
or more lowercase ASCII letters, digits, or underscores. Character order is
preserved where roster order matters.
Every entry has `player.name`, `character.name`, and a non-empty
`character.classes` list. Class entries require a non-empty `name` and may
include a positive integer `level`. The optional, intentionally singular
`character.alias` field is a list. Names, aliases, and class names must be
non-empty, trimmed display strings without control characters. Character names
and aliases must be unique across the full roster under Unicode-aware
case-insensitive comparison; player names may repeat.
The document has exactly one YAML document and accepts no unknown fields. A
wrong or malformed `schema_version` is an error.
## Legacy migration boundary
An unversioned party input remains supported only as opaque legacy reference
material while campaigns migrate. It requires a separate `players_file` and
retains the existing session override behavior. It cannot be mixed with a
canonical party: canonical campaigns must omit `players_file`, and sessions
must not override their party or players inputs.
Use the canonical document for new campaigns. The configuration rules and
source-relative path behavior are defined in the [Configuration Reference](../config.md).

View File

@@ -52,6 +52,14 @@ download retain that exact pipeline object and its private provenance. Removing
a temporary downloaded session file therefore cannot invalidate the resolved
pipeline or campaign context.
Campaign context construction also reads and classifies the campaign-owned
party source through `ParseParty`. A canonical party retains its raw bytes and
normalized roster in runtime-only `ResolvedParty` provenance, while a legacy
party remains opaque. Canonical resolution creates a virtual
`derived_from_party` players input and rejects competing campaign or session
players files and session party overrides. The compact legacy compatibility
path resolves the effective campaign/session party and players files together.
## Canonical Party Domain
`ParseParty` is the package-owned boundary for classifying a party source.
@@ -103,3 +111,6 @@ confirming local session resolution retains the original pipeline. Other
configuration tests continue to protect defaults and validation after assembly.
`party_test.go` protects the versioned party schema, domain invariants, and
deterministic players projection without involving campaign or runtime wiring.
`party_resolution_test.go` protects campaign-owned party loading, canonical
input restrictions, legacy overrides, source provenance, and virtual players
input selection.

View File

@@ -655,7 +655,7 @@ resolution.
## Stage 11 — Campaign Party Resolution And Isolated Legacy Mode
**Status: Pending**
**Status: Completed**
### Goal