Skip to content

feat(taxonomy): centralize term resolution with datamachine/resolve-term ability#89

Merged
chubes4 merged 4 commits intoExtra-Chill:mainfrom
saraichinwag:feat/centralize-taxonomy-resolution
Feb 3, 2026
Merged

feat(taxonomy): centralize term resolution with datamachine/resolve-term ability#89
chubes4 merged 4 commits intoExtra-Chill:mainfrom
saraichinwag:feat/centralize-taxonomy-resolution

Conversation

@saraichinwag
Copy link
Copy Markdown
Contributor

Summary

Fixes the numeric category bug (e.g., category '11' created instead of resolving to Spirituality).

Root Cause

Multiple code paths had their own term resolution logic:

  • TaxonomyHandler.findOrCreateTerm() - had the fix (ID/name/slug)
  • CreateTaxonomyTerm tool - only checked by name ❌
  • AssignTaxonomyTerm tool - had its own resolveTerm()

When AI output category: "11", the CreateTaxonomyTerm tool would:

  1. Check get_term_by('name', '11') → not found (term 11 is named 'Spirituality')
  2. Create new term named '11' ❌

Solution

Single source of truth: datamachine/resolve-term ability

Resolution Order (centralized)

  1. If numeric → get_term() by ID
  2. Try get_term_by('name')
  3. Try get_term_by('slug')
  4. If create=truewp_insert_term()

New Files

  • inc/Abilities/Taxonomy/ResolveTermAbility.php - Core ability
  • inc/Abilities/TaxonomyAbilities.php - Facade
  • Additional CRUD abilities (get, create, update, delete)

Updated Callers

All now use ResolveTermAbility::resolve():

  • TaxonomyHandler.findOrCreateTerm()
  • AssignTaxonomyTerm.resolveTerm()
  • CreateTaxonomyTerm existence check
  • CreateTaxonomyTerm.resolveParentTerm()

Testing

With this change, if AI outputs category: "11":

  1. ResolveTermAbility::resolve('11', 'category')
  2. is_numeric('11') → true
  3. get_term(11, 'category') → returns Spirituality ✅

No more drift. One fix applies everywhere.

…erm ability

Fixes the 'numeric category 11' bug by creating a single source of truth
for taxonomy term resolution.

## New Ability
- datamachine/resolve-term: Resolves terms by ID, name, or slug with
  optional creation. All code paths now use this ability.

## Architecture
- ResolveTermAbility: Core ability with static helper for internal use
- TaxonomyAbilities: Facade registering all taxonomy abilities
- Additional CRUD abilities: get, create, update, delete taxonomy terms

## Updated Callers
- TaxonomyHandler.findOrCreateTerm() → uses ResolveTermAbility::resolve()
- AssignTaxonomyTerm.resolveTerm() → uses ResolveTermAbility::resolve()
- CreateTaxonomyTerm existence check → uses ResolveTermAbility::resolve()
- CreateTaxonomyTerm.resolveParentTerm() → uses ResolveTermAbility::resolve()

## Resolution Order (centralized)
1. If numeric → get_term() by ID
2. Try get_term_by('name')
3. Try get_term_by('slug')
4. If create=true → wp_insert_term()

No more drift. One fix applies everywhere.
- Register ResolveTermAbility on datamachine_register_abilities hook
- Use execute_callback and permission_callback (match other abilities)
- Use proper input_schema with type/properties/required structure
- Replace local SYSTEM_TAXONOMIES with TaxonomyHandler::shouldSkipTaxonomy()
- Update/Delete abilities now use ResolveTermAbility::resolve()
- Remove duplicate resolveTerm methods from Update/Delete abilities
@saraichinwag
Copy link
Copy Markdown
Contributor Author

Addressed review feedback (0d830cc7):

ResolveTermAbility registration:

  • Changed hook from wp_abilities_initdatamachine_register_abilities
  • Changed callbackexecute_callback
  • Added permission_callback
  • Fixed input_schema structure (type/properties/required)

Single source of truth:

  • Removed local SYSTEM_TAXONOMIES constant
  • Now uses TaxonomyHandler::shouldSkipTaxonomy()

Update/Delete abilities:

  • Both now use ResolveTermAbility::resolve() instead of local resolveTerm() methods
  • Removed duplicate resolveTerm() methods (saves 50+ lines)

Ready for re-review.

…tterns

- Add WP_Ability class existence check in constructor
- Use did_action('wp_abilities_api_init') pattern for registration
- Add 'meta' => ['show_in_rest' => true] for REST exposure
- Match pattern used in GetTaxonomyTermsAbility and other abilities
GetTaxonomyTermsAbility has optional taxonomy param - only call
shouldSkipTaxonomy when taxonomy is non-empty to avoid TypeError.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants