topdata: stop hardcoding the skills dataset location
build-binaries / build-binaries (pull_request) Successful in 2m9s
test / test (pull_request) Successful in 1m23s

The skills dataset moved into a tree (skills/core, skills/specs, ...).
Resolve it data-driven instead of by name:

- The required-family check no longer pins skill focus families to
  dataset "skills"; the family spec's child_source names the dataset.
- Affinity generation and display names use the skill_focus spec's
  child_source dataset.
- Child slugs derive from the row key's own namespace (text after the
  first colon) instead of assuming namespace == dataset path.
- The wiki loader accepts both layouts ("skills", then "skills/core").

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-19 09:25:39 +02:00
co-authored by Claude Fable 5
parent 24e57457b0
commit adffb1e11d
3 changed files with 35 additions and 13 deletions
+18 -3
View File
@@ -2317,6 +2317,18 @@ func (c *featGeneratedContext) familyHasExistingRows(familyKey string) bool {
return false return false
} }
// skillsDatasetName returns the dataset the skill_focus family sources its
// children from. The skills dataset location is data-driven via that spec;
// "skills" is only the fallback when no spec is loaded.
func (c *featGeneratedContext) skillsDatasetName() string {
for key, spec := range c.familySpecs {
if normalizeKeyIdentity(key) == "skillfocus" && spec.ChildSource.Dataset != "" {
return spec.ChildSource.Dataset
}
}
return "skills"
}
func (c *featGeneratedContext) datasetRows(name string) (map[string]map[string]any, error) { func (c *featGeneratedContext) datasetRows(name string) (map[string]map[string]any, error) {
if rows, ok := c.rowsByDataset[name]; ok { if rows, ok := c.rowsByDataset[name]; ok {
return rows, nil return rows, nil
@@ -2555,7 +2567,10 @@ func buildFamilyExpansionGeneratedModule(path string, obj map[string]any, ctx *f
if !include { if !include {
continue continue
} }
slug := strings.TrimPrefix(sourceKey, spec.ChildSource.Dataset+":") slug := sourceKey
if idx := strings.Index(slug, ":"); idx >= 0 {
slug = slug[idx+1:]
}
featKey, rowID, hasID, err := ctx.resolveGeneratedFeatIdentity(spec, slug, row) featKey, rowID, hasID, err := ctx.resolveGeneratedFeatIdentity(spec, slug, row)
if err != nil { if err != nil {
return nil, fmt.Errorf("generated feat file %s: %w", path, err) return nil, fmt.Errorf("generated feat file %s: %w", path, err)
@@ -2655,7 +2670,7 @@ func buildRacialtypesSkillAffinityModule(ctx *featGeneratedContext) (map[string]
if len(grants) == 0 { if len(grants) == 0 {
return nil, nil return nil, nil
} }
skillRows, err := ctx.datasetRows("skills") skillRows, err := ctx.datasetRows(ctx.skillsDatasetName())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -2854,7 +2869,7 @@ func (c *featGeneratedContext) displayNameForGeneratedSource(dataset string, row
return text return text
} }
switch dataset { switch dataset {
case "skills": case c.skillsDatasetName():
return displayNameForSkill(row) return displayNameForSkill(row)
case "baseitems": case "baseitems":
return displayNameForBaseitem(row) return displayNameForBaseitem(row)
+9 -4
View File
@@ -1867,8 +1867,10 @@ func validateGeneratedFeatFamilies(dataDir string, report *ValidationReport) {
} }
required := []requiredFeatFamily{ required := []requiredFeatFamily{
{FamilyKey: "skillfocus", Dataset: "skills", Predicate: "accessible"}, // skill families: the source dataset is data-driven (family spec), only
{FamilyKey: "greaterskillfocus", Dataset: "skills", Predicate: "accessible"}, // the accessibility predicate is required
{FamilyKey: "skillfocus", Predicate: "accessible"},
{FamilyKey: "greaterskillfocus", Predicate: "accessible"},
{FamilyKey: "weaponfocus", Dataset: "baseitems", Column: "WeaponFocusFeat"}, {FamilyKey: "weaponfocus", Dataset: "baseitems", Column: "WeaponFocusFeat"},
{FamilyKey: "weaponspecialization", Dataset: "baseitems", Column: "WeaponSpecializationFeat"}, {FamilyKey: "weaponspecialization", Dataset: "baseitems", Column: "WeaponSpecializationFeat"},
{FamilyKey: "improvedcritical", Dataset: "baseitems", Column: "WeaponImprovedCriticalFeat"}, {FamilyKey: "improvedcritical", Dataset: "baseitems", Column: "WeaponImprovedCriticalFeat"},
@@ -1882,7 +1884,7 @@ func validateGeneratedFeatFamilies(dataDir string, report *ValidationReport) {
if !ok { if !ok {
continue continue
} }
if spec.ChildSource.Dataset != requirement.Dataset || if (requirement.Dataset != "" && spec.ChildSource.Dataset != requirement.Dataset) ||
spec.ChildSource.Column != requirement.Column || spec.ChildSource.Column != requirement.Column ||
spec.ChildSource.Predicate != requirement.Predicate { spec.ChildSource.Predicate != requirement.Predicate {
report.Diagnostics = append(report.Diagnostics, Diagnostic{ report.Diagnostics = append(report.Diagnostics, Diagnostic{
@@ -2081,7 +2083,10 @@ func validateGeneratedFeatFamilyCompleteness(path string, spec familyExpansionSp
} }
if include { if include {
if familyAllowlist { if familyAllowlist {
slug := strings.TrimPrefix(sourceKey, spec.ChildSource.Dataset+":") slug := sourceKey
if idx := strings.Index(slug, ":"); idx >= 0 {
slug = slug[idx+1:]
}
featKey, _, _, err := ctx.resolveGeneratedFeatIdentity(spec, slug, row) featKey, _, _, err := ctx.resolveGeneratedFeatIdentity(spec, slug, row)
if err != nil { if err != nil {
report.Diagnostics = append(report.Diagnostics, Diagnostic{ report.Diagnostics = append(report.Diagnostics, Diagnostic{
+8 -6
View File
@@ -569,11 +569,13 @@ func loadWikiContext(dataDir, sourceDir string) (*wikiContext, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
loadBase := func(name string) (nativeCollectedDataset, bool, error) { loadBase := func(names ...string) (nativeCollectedDataset, bool, error) {
for _, dataset := range datasets { for _, name := range names {
if dataset.Name == name { for _, dataset := range datasets {
collected, err := collectNativeDataset(dataset) if dataset.Name == name {
return collected, true, err collected, err := collectNativeDataset(dataset)
return collected, true, err
}
} }
} }
return nativeCollectedDataset{}, false, nil return nativeCollectedDataset{}, false, nil
@@ -583,7 +585,7 @@ func loadWikiContext(dataDir, sourceDir string) (*wikiContext, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
skillDataset, skillOK, err := loadBase("skills") skillDataset, skillOK, err := loadBase("skills", "skills/core")
if err != nil { if err != nil {
return nil, err return nil, err
} }