claude: fold in old sow-tools
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
# `feat` Generated Families Contract
|
||||
|
||||
## Status Snapshot
|
||||
|
||||
Current state as of 2026-05-13:
|
||||
|
||||
- Implemented for the current native `feat` pipeline.
|
||||
- Native tests cover generated-family expansion, family-owned shared defaults,
|
||||
masterfeat-backed inheritance, and lock/TLK identity preservation.
|
||||
- The one explicitly deferred item in this contract is still current:
|
||||
`feat.2da` remains native-built but excluded from `compare-topdata`
|
||||
reference-catalog coverage through `compare_reference: false`.
|
||||
|
||||
Canonical authored paths:
|
||||
|
||||
- `topdata/data/feat/base.json`
|
||||
- `topdata/data/feat/lock.json`
|
||||
- `topdata/data/feat/generated/skill_focus.json`
|
||||
- `topdata/data/feat/generated/greater_skill_focus.json`
|
||||
- `topdata/data/feat/generated/weapon_focus.json`
|
||||
- `topdata/data/feat/generated/weapon_specialization.json`
|
||||
- `topdata/data/feat/generated/greater_weapon_focus.json`
|
||||
- `topdata/data/feat/generated/greater_weapon_specialization.json`
|
||||
- `topdata/data/feat/generated/improved_critical.json`
|
||||
- `topdata/data/feat/generated/overwhelming_critical.json`
|
||||
- `topdata/data/feat/modules/activecombat/core.json`
|
||||
- `topdata/data/feat/modules/activecombat/specialattacks.json`
|
||||
- `topdata/data/feat/modules/class/core.json`
|
||||
- `topdata/data/feat/modules/combat/core.json`
|
||||
- `topdata/data/feat/modules/defensive/core.json`
|
||||
- `topdata/data/feat/modules/magical/core.json`
|
||||
- `topdata/data/feat/modules/other/core.json`
|
||||
- `topdata/data/feat/modules/proficiency/core.json`
|
||||
- `topdata/data/feat/modules/racial/core.json`
|
||||
- `topdata/data/feat/modules/removedandhidden/core.json`
|
||||
- `topdata/data/feat/modules/skillfeat/affinity.json`
|
||||
- `topdata/data/feat/modules/skillfeat/focus.json`
|
||||
- `topdata/data/feat/modules/skillfeat/greaterfocus.json`
|
||||
- `topdata/data/feat/modules/skillfeat/other.json`
|
||||
|
||||
`skill_affinity` is no longer authored as a generated feat file. It is synthesized from
|
||||
canonical `racialtypes` race grants.
|
||||
|
||||
Compact family-expansion shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"family": "skill_focus",
|
||||
"family_key": "skillfocus",
|
||||
"template": "masterfeats:skillfocus",
|
||||
"name_prefix": "Skill Focus",
|
||||
"label_prefix": "FEAT_SKILL_FOCUS",
|
||||
"constant_prefix": "FEAT_SKILL_FOCUS",
|
||||
"default_fields": {
|
||||
"DESCRIPTION": {
|
||||
"tlk": {
|
||||
"key": "masterfeats:skillfocus.description",
|
||||
"text": "..."
|
||||
}
|
||||
},
|
||||
"CRValue": "0.5",
|
||||
"ReqSkillMinRanks": "1",
|
||||
"TOOLSCATEGORIES": "6"
|
||||
},
|
||||
"apply_after_modules": true,
|
||||
"child_ref_field": "REQSKILL",
|
||||
"child_source": {
|
||||
"dataset": "skills",
|
||||
"predicate": "accessible"
|
||||
},
|
||||
"title_style": {
|
||||
"child_case": "lower",
|
||||
"child_parenthetical": "comma"
|
||||
},
|
||||
"overrides": {
|
||||
"skills:craft_alchemy": {
|
||||
"ICON": "ife_foc_alchm"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Weapon family shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"family": "weapon_focus",
|
||||
"family_key": "weaponfocus",
|
||||
"template": "masterfeats:weaponfocus",
|
||||
"name_prefix": "Weapon Focus",
|
||||
"label_prefix": "FEAT_WEAPON_FOCUS",
|
||||
"constant_prefix": "FEAT_WEAPON_FOCUS",
|
||||
"legacy_family_keys": ["epic_weapon_focus"],
|
||||
"default_fields": {
|
||||
"DESCRIPTION": {
|
||||
"tlk": {
|
||||
"key": "masterfeats:weaponfocus.description",
|
||||
"text": "..."
|
||||
}
|
||||
},
|
||||
"MINATTACKBONUS": "1",
|
||||
"TOOLSCATEGORIES": "1"
|
||||
},
|
||||
"apply_after_modules": true,
|
||||
"identity_source": "child_source_value",
|
||||
"child_source": {
|
||||
"dataset": "baseitems",
|
||||
"column": "WeaponFocusFeat"
|
||||
},
|
||||
"title_style": {
|
||||
"child_case": "lower",
|
||||
"child_parenthetical": "preserve"
|
||||
},
|
||||
"overrides": {
|
||||
"baseitems:heavymace": {
|
||||
"ICON": "ife_wepfoc_Lma"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
- any generated feat file with family-expansion fields is treated as an authored
|
||||
family-expansion definition
|
||||
- `family` is descriptive only; the builder does not hardcode family names
|
||||
- family behavior is driven by authored fields such as `template_fields`,
|
||||
`default_fields`, `apply_after_modules`, `identity_source`, `child_ref_field`,
|
||||
`auto_prereq_fields`, and optional `title_style`
|
||||
- `default_fields` is a family-wide shared layer; it applies to existing and newly
|
||||
generated child rows before per-child overrides are merged
|
||||
- `apply_after_modules: true` makes the generated family the final authoritative shared
|
||||
layer for legacy explicitly authored child rows, so shared baselines can live in the
|
||||
generated family file instead of `masterfeats` overrides
|
||||
- `legacy_family_keys` declares old generated-family prefixes whose existing row IDs
|
||||
should be inherited by the current family; this is how renamed families such as
|
||||
`greater_skill_focus` retaining vanilla `epic_skill_focus` rows preserve hardcoded
|
||||
engine behavior without hardcoding the rename in toolkit logic
|
||||
- family-expansion files must declare `template`, `family_key`, and `child_source.dataset`
|
||||
- `child_source.column` is used when expansion is gated by a non-null source field
|
||||
- `child_source.predicate: "accessible"` currently means `HideFromLevelUp != 1`
|
||||
- `title_style` only changes generated child display text while composing titles;
|
||||
generated feat keys, TLK keys, row IDs, family metadata, and source references
|
||||
remain tied to generated family identity
|
||||
- generated family title text is authoritative for family-managed existing rows
|
||||
as well as newly allocated rows, so in-game TLK names and generated wiki titles
|
||||
are derived from the same normalized `FEAT` text
|
||||
- `title_style.child_case` defaults to `preserve`; `lower` lowercases the resolved
|
||||
child display text after parenthetical formatting
|
||||
- `title_style.child_parenthetical` defaults to `preserve`; `comma` flattens one
|
||||
child parenthetical suffix such as `Knowledge (local)` into
|
||||
`Knowledge, local` before the child text is wrapped by the family title
|
||||
- unsupported `title_style` values fail generated-family validation instead of
|
||||
silently changing title behavior
|
||||
- skill focus families intentionally do not set `allow_existing_only`, so any new
|
||||
accessible skill automatically receives matching focus rows
|
||||
- compact `overrides` are keyed by source dataset key:
|
||||
- `skills:*` for skill families
|
||||
- `baseitems:*` for weapon families
|
||||
- generated rows carry `meta.family` so the underscore family rule is preserved even
|
||||
before wiki generation exists
|
||||
- emitted feat keys must prefer existing canonical lock/TLK identities over newly
|
||||
derived child spellings
|
||||
- manual and irregular feat content continues to live under `feat/modules/`
|
||||
- generated families and module-authored feats are merged into the same final `feat.2da`
|
||||
pipeline and share inheritance, override, TLK, and family-metadata behavior
|
||||
|
||||
Current rollout policy:
|
||||
|
||||
- `feat.2da` still builds natively
|
||||
- `feat.2da` is still excluded from `compare-topdata` output-catalog self-check coverage
|
||||
via `compare_reference: false`
|
||||
- manual and irregular feat families remain authored directly in modules rather than
|
||||
compact generated-family files
|
||||
Reference in New Issue
Block a user