topdata: stop hardcoding the skills dataset location (#43)

sow-topdata moved skills into a tree (skills/core, skills/specs, skillcats, skillspecranks), which broke validate/build: the generated feat families hardcoded dataset "skills".

Resolution is now data-driven: the family spec's child_source names the dataset, child slugs come from the row key's own namespace, and the wiki loader accepts both the flat and tree layouts (topdata main still uses the flat one until the overhaul merges).

Verified against the skill-grouping-overhaul topdata branch: validate/build/wiki all clean with zero lock churn, focus feats stay on their adopted rows.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Reviewed-on: #43
Co-authored-by: vickydotbat <vickydotbat@tutamail.com>
Co-committed-by: vickydotbat <vickydotbat@tutamail.com>
This commit is contained in:
2026-07-19 07:33:19 +00:00
committed by archvillainette
parent 24e57457b0
commit 26db768c2a
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
} }