Remove legacy party configuration
This commit is contained in:
@@ -1,57 +0,0 @@
|
||||
package stage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"gitea.maximumdirect.net/eric/narratio/internal/artifactpolicy"
|
||||
"gitea.maximumdirect.net/eric/narratio/internal/artifacts"
|
||||
"gitea.maximumdirect.net/eric/narratio/internal/config"
|
||||
"gitea.maximumdirect.net/eric/narratio/internal/manifest"
|
||||
)
|
||||
|
||||
// materializeLegacyPartyInputs preserves the removable compatibility path for
|
||||
// opaque party and explicit players files. Remove it when legacy roster inputs
|
||||
// are no longer supported.
|
||||
func materializeLegacyPartyInputs(
|
||||
store artifacts.Store,
|
||||
cfg *config.Config,
|
||||
inputsDir string,
|
||||
party, players artifactpolicy.PreparedInputSourceDescriptor,
|
||||
) ([]manifest.InputRecord, error) {
|
||||
playersInput := stableInputSource(cfg.StableInputs.PlayersFile, cfg.Session.Inputs.PlayersFile, cfg.SessionPath)
|
||||
partyInput := stableInputSource(cfg.StableInputs.PartyFile, cfg.Session.Inputs.PartyFile, cfg.SessionPath)
|
||||
playersSource, err := resolveConfigRelativePath(playersInput)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("players path: %w", err)
|
||||
}
|
||||
partySource, err := resolveConfigRelativePath(partyInput)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("party path: %w", err)
|
||||
}
|
||||
for _, required := range []struct {
|
||||
path string
|
||||
name string
|
||||
}{
|
||||
{path: playersSource, name: "players.yml"},
|
||||
{path: partySource, name: "party.yml"},
|
||||
} {
|
||||
if err := requireFile(required.path, required.name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
partyPath := filepath.Join(inputsDir, party.Filename)
|
||||
partyChecksum, err := copyFileIfChanged(store, partySource, partyPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("materialize party: %w", err)
|
||||
}
|
||||
playersPath := filepath.Join(inputsDir, players.Filename)
|
||||
playersChecksum, err := copyFileIfChanged(store, playersSource, playersPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("materialize players: %w", err)
|
||||
}
|
||||
return []manifest.InputRecord{
|
||||
{Kind: party.ManifestKind, Path: partyPath, Checksum: partyChecksum, Source: partyInput.Source},
|
||||
{Kind: players.ManifestKind, Path: playersPath, Checksum: playersChecksum, Source: playersInput.Source},
|
||||
}, nil
|
||||
}
|
||||
@@ -23,10 +23,7 @@ func materializePreparedPartyInputs(store artifacts.Store, cfg *config.Config, i
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("prepared-input descriptor %q is unavailable", artifactpolicy.SourceInputParty)
|
||||
}
|
||||
if cfg.Party.Mode == config.PartyModeCanonical {
|
||||
return materializeCanonicalPartyInputs(store, cfg.Party, inputsDir, party, players)
|
||||
}
|
||||
return materializeLegacyPartyInputs(store, cfg, inputsDir, party, players)
|
||||
return materializeCanonicalPartyInputs(store, cfg.Party, inputsDir, party, players)
|
||||
}
|
||||
|
||||
func materializeCanonicalPartyInputs(
|
||||
|
||||
@@ -132,43 +132,17 @@ func currentPreparePartySourceInputs(env *Env) ([]prepareExpectedInput, error) {
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("players prepared-input descriptor is unavailable")
|
||||
}
|
||||
if env.Config.Party.Mode == config.PartyModeCanonical {
|
||||
if env.Config.Party.Canonical == nil || len(env.Config.Party.Canonical.Raw) == 0 {
|
||||
return nil, fmt.Errorf("canonical party data is unavailable")
|
||||
}
|
||||
playersBytes, err := env.Config.Party.Canonical.PlayersYAML()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []prepareExpectedInput{
|
||||
{kind: party.ManifestKind, checksum: checksumPrepareBytes(env.Config.Party.Canonical.Raw)},
|
||||
{kind: players.ManifestKind, checksum: checksumPrepareBytes(playersBytes)},
|
||||
}, nil
|
||||
if env.Config.Party.Canonical == nil || len(env.Config.Party.Canonical.Raw) == 0 {
|
||||
return nil, fmt.Errorf("canonical party data is unavailable")
|
||||
}
|
||||
|
||||
cfg := env.Config
|
||||
inputs := []struct {
|
||||
kind string
|
||||
resolved config.ResolvedInputFile
|
||||
fallback string
|
||||
}{
|
||||
{kind: party.ManifestKind, resolved: cfg.StableInputs.PartyFile, fallback: cfg.Session.Inputs.PartyFile},
|
||||
{kind: players.ManifestKind, resolved: cfg.StableInputs.PlayersFile, fallback: cfg.Session.Inputs.PlayersFile},
|
||||
playersBytes, err := env.Config.Party.Canonical.PlayersYAML()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
expected := make([]prepareExpectedInput, 0, len(inputs))
|
||||
for _, input := range inputs {
|
||||
resolved := stableInputSource(input.resolved, input.fallback, cfg.SessionPath)
|
||||
path, err := resolveConfigRelativePath(resolved)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
checksum, err := env.ArtifactStore.Checksum(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
expected = append(expected, prepareExpectedInput{kind: input.kind, checksum: checksum})
|
||||
}
|
||||
return expected, nil
|
||||
return []prepareExpectedInput{
|
||||
{kind: party.ManifestKind, checksum: checksumPrepareBytes(env.Config.Party.Canonical.Raw)},
|
||||
{kind: players.ManifestKind, checksum: checksumPrepareBytes(playersBytes)},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func currentPrepareAudioSourceInputs(ctx context.Context, env *Env, m *manifest.Manifest) ([]prepareExpectedInput, error) {
|
||||
|
||||
@@ -18,9 +18,13 @@ func TestPrepareResumeValidatesCurrentLocalSourcesAndPreparedCopies(t *testing.T
|
||||
mutate func(*testing.T, *Env, string)
|
||||
}{
|
||||
{
|
||||
name: "stable input bytes changed",
|
||||
name: "canonical party bytes changed",
|
||||
mutate: func(t *testing.T, env *Env, _ string) {
|
||||
writeFile(t, filepath.Join(filepath.Dir(env.Config.CampaignPath), "party.yml"), "changed: true\n")
|
||||
party, err := config.ParseParty([]byte("schema_version: narratio.party.v1\ncharacters:\n changed:\n player: {name: Eric}\n character:\n name: Changed\n classes: [{name: wizard}]\n"))
|
||||
if err != nil {
|
||||
t.Fatalf("ParseParty() error = %v", err)
|
||||
}
|
||||
env.Config.Party.Canonical = party
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -58,7 +62,7 @@ func TestPrepareResumeAcceptsRelocatedEquivalentLocalSource(t *testing.T) {
|
||||
env, m, _ := prepareResumeLocalFixture(t)
|
||||
cfgDir := filepath.Dir(env.Config.CampaignPath)
|
||||
relocated := filepath.Join(cfgDir, "relocated-party.yml")
|
||||
writeFile(t, relocated, "[]\n")
|
||||
writeFile(t, relocated, "schema_version: narratio.party.v1\ncharacters:\n arannis:\n player: {name: Eric}\n character:\n name: Arannis\n classes: [{name: wizard}]\n")
|
||||
env.Config.StableInputs.PartyFile.Path = "./relocated-party.yml"
|
||||
assertPrepareResume(t, env, m, true)
|
||||
}
|
||||
@@ -135,7 +139,7 @@ func setCanonicalPrepareParty(t *testing.T, env *Env, raw []byte) {
|
||||
t.Fatalf("ParseParty() error = %v", err)
|
||||
}
|
||||
env.Config.Party = config.ResolvedParty{
|
||||
Mode: config.PartyModeCanonical, Source: config.PartySource{Source: "campaign_config"}, Canonical: document.Canonical,
|
||||
Source: config.PartySource{Source: "campaign_config"}, Canonical: document,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -177,19 +177,14 @@ characters:
|
||||
if err != nil {
|
||||
t.Fatalf("ParseParty() error = %v", err)
|
||||
}
|
||||
if err := os.Remove(filepath.Join(filepath.Dir(env.Config.SessionPath), "players.yml")); err != nil {
|
||||
t.Fatalf("remove legacy players source: %v", err)
|
||||
}
|
||||
env.Config.Party = config.ResolvedParty{
|
||||
Mode: config.PartyModeCanonical,
|
||||
Canonical: document.Canonical,
|
||||
Canonical: document,
|
||||
Source: config.PartySource{
|
||||
Path: filepath.Join(filepath.Dir(env.Config.CampaignPath), "party.yml"),
|
||||
ConfigPath: env.Config.CampaignPath,
|
||||
Source: "campaign_config",
|
||||
},
|
||||
}
|
||||
env.Config.StableInputs.PlayersFile = config.ResolvedInputFile{Source: "derived_from_party"}
|
||||
writeFile(t, filepath.Join(filepath.Dir(env.Config.SessionPath), "audio", "alice.flac"), "audio")
|
||||
|
||||
if _, err := (prepareStage{}).Run(context.Background(), env, m); err != nil {
|
||||
@@ -201,7 +196,7 @@ characters:
|
||||
if got, err := os.ReadFile(preparedParty); err != nil || !bytes.Equal(got, partyBytes) {
|
||||
t.Fatalf("prepared party = %q, %v; want unchanged canonical bytes", got, err)
|
||||
}
|
||||
wantPlayers, err := document.Canonical.PlayersYAML()
|
||||
wantPlayers, err := document.PlayersYAML()
|
||||
if err != nil {
|
||||
t.Fatalf("PlayersYAML() error = %v", err)
|
||||
}
|
||||
@@ -228,30 +223,6 @@ characters:
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrepareStageLegacyPartyInputsRemainOpaqueCopies(t *testing.T) {
|
||||
env, m := setupPrepareEnv(t)
|
||||
root := filepath.Dir(env.Config.SessionPath)
|
||||
playersBytes := []byte("legacy players\n")
|
||||
partyBytes := []byte("legacy party\n")
|
||||
writeFile(t, filepath.Join(root, "players.yml"), string(playersBytes))
|
||||
writeFile(t, filepath.Join(root, "party.yml"), string(partyBytes))
|
||||
writeFile(t, filepath.Join(root, "audio", "alice.flac"), "audio")
|
||||
env.Config.Party = config.ResolvedParty{Mode: config.PartyModeLegacy}
|
||||
if _, err := (prepareStage{}).Run(context.Background(), env, m); err != nil {
|
||||
t.Fatalf("prepare.Run() error = %v", err)
|
||||
}
|
||||
paths := sessionPathsForEnv(env, m.SessionID)
|
||||
if got, err := os.ReadFile(filepath.Join(paths.InputsDir, "party.yml")); err != nil || !bytes.Equal(got, partyBytes) {
|
||||
t.Fatalf("prepared legacy party = %q, %v", got, err)
|
||||
}
|
||||
if got, err := os.ReadFile(filepath.Join(paths.InputsDir, "players.yml")); err != nil || !bytes.Equal(got, playersBytes) {
|
||||
t.Fatalf("prepared legacy players = %q, %v", got, err)
|
||||
}
|
||||
if got := findManifestInput(t, m.Inputs, "players").Source; got != "campaign_config" {
|
||||
t.Fatalf("legacy players source = %q, want campaign_config", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrepareStageMaterializesSpellCatalogWithProvenance(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -888,15 +859,18 @@ inputs:
|
||||
speakers_file: ./speakers.yml
|
||||
autocorrect_file: ./autocorrect.yml
|
||||
glossary_file: ./glossary.yml
|
||||
players_file: ./players.yml
|
||||
party_file: ./party.yml
|
||||
`)
|
||||
writeFile(t, sessionPath, "session_id: 2026-05-03\n")
|
||||
writeFile(t, filepath.Join(cfgDir, "speakers.yml"), "alice: alice.flac\n")
|
||||
writeFile(t, filepath.Join(cfgDir, "autocorrect.yml"), "[]\n")
|
||||
writeFile(t, filepath.Join(cfgDir, "glossary.yml"), "[]\n")
|
||||
writeFile(t, filepath.Join(cfgDir, "players.yml"), "[]\n")
|
||||
writeFile(t, filepath.Join(cfgDir, "party.yml"), "[]\n")
|
||||
partyBytes := []byte("schema_version: narratio.party.v1\ncharacters:\n arannis:\n player: {name: Eric}\n character:\n name: Arannis\n classes: [{name: wizard}]\n")
|
||||
writeFile(t, filepath.Join(cfgDir, "party.yml"), string(partyBytes))
|
||||
party, err := config.ParseParty(partyBytes)
|
||||
if err != nil {
|
||||
t.Fatalf("ParseParty() error = %v", err)
|
||||
}
|
||||
|
||||
cfg := &config.Config{
|
||||
Pipeline: &config.PipelineConfig{
|
||||
@@ -907,6 +881,9 @@ inputs:
|
||||
SessionPath: sessionPath,
|
||||
CampaignPath: campaignPath,
|
||||
PipelinePath: pipelinePath,
|
||||
Party: config.ResolvedParty{Canonical: party, Source: config.PartySource{
|
||||
Path: filepath.Join(cfgDir, "party.yml"), ConfigPath: campaignPath, Source: "campaign_config",
|
||||
}},
|
||||
Session: &config.SessionConfig{
|
||||
SessionID: "2026-05-03",
|
||||
Campaign: "sample-campaign",
|
||||
@@ -930,11 +907,6 @@ inputs:
|
||||
ConfigPath: campaignPath,
|
||||
Source: "campaign_config",
|
||||
},
|
||||
PlayersFile: config.ResolvedInputFile{
|
||||
Path: "./players.yml",
|
||||
ConfigPath: campaignPath,
|
||||
Source: "campaign_config",
|
||||
},
|
||||
PartyFile: config.ResolvedInputFile{
|
||||
Path: "./party.yml",
|
||||
ConfigPath: campaignPath,
|
||||
|
||||
@@ -20,7 +20,6 @@ type prepareSemanticConfig struct {
|
||||
CampaignID string `json:"campaign_id"`
|
||||
SessionID string `json:"session_id"`
|
||||
PreviousSession string `json:"previous_session_id"`
|
||||
PartyMode string `json:"party_mode"`
|
||||
PlayersSchema string `json:"players_schema_version,omitempty"`
|
||||
Audio prepareAudioSelection `json:"audio"`
|
||||
StableInputs []prepareStableInputSelection `json:"stable_inputs"`
|
||||
@@ -106,21 +105,17 @@ func buildPrepareSemanticConfig(env *Env) (prepareSemanticConfig, error) {
|
||||
if err != nil {
|
||||
return prepareSemanticConfig{}, fmt.Errorf("resolve previous artifact requirements: %w", err)
|
||||
}
|
||||
partyMode := cfg.Party.Mode
|
||||
if partyMode == "" {
|
||||
if cfg.Party.Canonical == nil {
|
||||
return prepareSemanticConfig{}, fmt.Errorf("prepare semantic configuration requires a resolved canonical party")
|
||||
}
|
||||
payload := prepareSemanticConfig{
|
||||
CampaignID: strings.TrimSpace(cfg.Session.Campaign),
|
||||
SessionID: strings.TrimSpace(cfg.Session.SessionID),
|
||||
PreviousSession: strings.TrimSpace(cfg.Session.PreviousSessionID),
|
||||
PartyMode: string(partyMode),
|
||||
PlayersSchema: config.PlayersSchemaVersion,
|
||||
Audio: prepareAudioSemantics(cfg),
|
||||
StableInputs: prepareStableInputSemantics(cfg),
|
||||
}
|
||||
if partyMode == config.PartyModeCanonical {
|
||||
payload.PlayersSchema = config.PlayersSchemaVersion
|
||||
}
|
||||
for _, requirement := range requirements {
|
||||
entry := preparePreviousRequirement{Name: strings.TrimSpace(requirement.Name), Required: requirement.Required}
|
||||
if cfg.Pipeline.Scriptorium != nil {
|
||||
@@ -159,8 +154,7 @@ func prepareStableInputSemantics(cfg *config.Config) []prepareStableInputSelecti
|
||||
prepareStableInput("speakers", stable.SpeakersFile, cfg.Session.Inputs.SpeakersFile),
|
||||
prepareStableInput("autocorrect", stable.AutocorrectFile, cfg.Session.Inputs.AutocorrectFile),
|
||||
prepareStableInput("glossary", stable.GlossaryFile, cfg.Session.Inputs.GlossaryFile),
|
||||
prepareStableInput("players", stable.PlayersFile, cfg.Session.Inputs.PlayersFile),
|
||||
prepareStableInput("party", stable.PartyFile, cfg.Session.Inputs.PartyFile),
|
||||
prepareStableInput("party", stable.PartyFile, ""),
|
||||
prepareStableInput("spell_catalog", stable.SpellCatalogFile, cfg.Session.Inputs.SpellCatalogFile),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,12 +49,11 @@ func TestPrepareSemanticConfigSensitivity(t *testing.T) {
|
||||
|
||||
func TestPrepareSemanticConfigRecordsCanonicalProjectionVersion(t *testing.T) {
|
||||
env := initialSemanticEnv()
|
||||
env.Config.Party.Mode = config.PartyModeCanonical
|
||||
payload, err := buildPrepareSemanticConfig(env)
|
||||
if err != nil {
|
||||
t.Fatalf("buildPrepareSemanticConfig() error = %v", err)
|
||||
}
|
||||
if payload.PartyMode != string(config.PartyModeCanonical) || payload.PlayersSchema != config.PlayersSchemaVersion {
|
||||
if payload.PlayersSchema != config.PlayersSchemaVersion {
|
||||
t.Fatalf("prepare semantic payload = %#v", payload)
|
||||
}
|
||||
}
|
||||
@@ -120,13 +119,13 @@ func initialSemanticEnv() *Env {
|
||||
Session: &config.SessionConfig{
|
||||
SessionID: "2026-05-03", Campaign: "campaign",
|
||||
Inputs: config.SessionInputsConfig{
|
||||
AudioFiles: []string{"/audio/speaker.flac"}, PartyFile: "party.yml",
|
||||
AudioFiles: []string{"/audio/speaker.flac"},
|
||||
},
|
||||
},
|
||||
StableInputs: config.ResolvedStableInputs{
|
||||
PartyFile: config.ResolvedInputFile{Path: "/campaign/party.yml", ConfigPath: "/campaign/campaign.yml", Source: "session_config"},
|
||||
},
|
||||
Party: config.ResolvedParty{Mode: config.PartyModeCanonical},
|
||||
Party: config.ResolvedParty{Canonical: &config.CanonicalParty{}},
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
@@ -401,12 +401,16 @@ func setupTranscribeEnv(t *testing.T, audioFiles []string) (*Env, *manifest.Mani
|
||||
campaignPath := filepath.Join(cfgDir, "campaign.yml")
|
||||
writeFile(t, sessionPath, "session_id: 2026-05-03\ncampaign: sample-campaign\n")
|
||||
writeFile(t, pipelinePath, "workspace:\n root: "+workspace+"\n")
|
||||
writeFile(t, campaignPath, "campaign_id: sample-campaign\ninputs:\n speakers_file: ./speakers.yml\n autocorrect_file: ./autocorrect.yml\n glossary_file: ./glossary.yml\n players_file: ./players.yml\n party_file: ./party.yml\n")
|
||||
writeFile(t, campaignPath, "campaign_id: sample-campaign\ninputs:\n speakers_file: ./speakers.yml\n autocorrect_file: ./autocorrect.yml\n glossary_file: ./glossary.yml\n party_file: ./party.yml\n")
|
||||
writeFile(t, filepath.Join(cfgDir, "speakers.yml"), "alice: alice.flac\n")
|
||||
writeFile(t, filepath.Join(cfgDir, "autocorrect.yml"), "[]\n")
|
||||
writeFile(t, filepath.Join(cfgDir, "glossary.yml"), "[]\n")
|
||||
writeFile(t, filepath.Join(cfgDir, "players.yml"), "[]\n")
|
||||
writeFile(t, filepath.Join(cfgDir, "party.yml"), "[]\n")
|
||||
partyBytes := []byte("schema_version: narratio.party.v1\ncharacters:\n arannis:\n player: {name: Eric}\n character:\n name: Arannis\n classes: [{name: wizard}]\n")
|
||||
writeFile(t, filepath.Join(cfgDir, "party.yml"), string(partyBytes))
|
||||
party, err := config.ParseParty(partyBytes)
|
||||
if err != nil {
|
||||
t.Fatalf("ParseParty() error = %v", err)
|
||||
}
|
||||
|
||||
retries := 3
|
||||
concurrency := 2
|
||||
@@ -415,6 +419,9 @@ func setupTranscribeEnv(t *testing.T, audioFiles []string) (*Env, *manifest.Mani
|
||||
CampaignPath: campaignPath,
|
||||
SessionPath: sessionPath,
|
||||
Campaign: &config.CampaignConfig{CampaignID: "sample-campaign"},
|
||||
Party: config.ResolvedParty{Canonical: party, Source: config.PartySource{
|
||||
Path: filepath.Join(cfgDir, "party.yml"), ConfigPath: campaignPath, Source: "campaign_config",
|
||||
}},
|
||||
Pipeline: &config.PipelineConfig{
|
||||
Workspace: config.WorkspaceConfig{Root: workspace},
|
||||
WhisperX: config.WhisperXConfig{
|
||||
@@ -434,15 +441,12 @@ func setupTranscribeEnv(t *testing.T, audioFiles []string) (*Env, *manifest.Mani
|
||||
SpeakersFile: "./speakers.yml",
|
||||
AutocorrectFile: "./autocorrect.yml",
|
||||
GlossaryFile: "./glossary.yml",
|
||||
PlayersFile: "./players.yml",
|
||||
PartyFile: "./party.yml",
|
||||
},
|
||||
},
|
||||
StableInputs: config.ResolvedStableInputs{
|
||||
SpeakersFile: config.ResolvedInputFile{Path: "./speakers.yml", ConfigPath: campaignPath, Source: "campaign_config"},
|
||||
AutocorrectFile: config.ResolvedInputFile{Path: "./autocorrect.yml", ConfigPath: campaignPath, Source: "campaign_config"},
|
||||
GlossaryFile: config.ResolvedInputFile{Path: "./glossary.yml", ConfigPath: campaignPath, Source: "campaign_config"},
|
||||
PlayersFile: config.ResolvedInputFile{Path: "./players.yml", ConfigPath: campaignPath, Source: "campaign_config"},
|
||||
PartyFile: config.ResolvedInputFile{Path: "./party.yml", ConfigPath: campaignPath, Source: "campaign_config"},
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user