Skip to main content

Feature Specification: Taxonomy System (Categories, Tags, Collections)

Feature ID: taxonomy-system Status: Retrospective Created: 2026-05-01 Last updated: 2026-05-01 Owner: Ever Works Team


1. Overview

Every work has a three-dimensional taxonomy:

  • Categories — primary classification, exactly one per item
  • Tags — descriptive labels, 0 or more per item
  • Collections — editorial groupings, 0 or 1 per item (optional)

Taxonomy is stored in the data repository as YAML files (categories.yml, tags.yml, collections.yml) and managed through WorkTaxonomyService. CRUD operations enforce role-based access control, auto-generate URL-friendly slug ids from names, validate name uniqueness case-insensitively, and persist by reading-modifying-writing the relevant YAML file with a single git commit.

2. User Scenarios

2.1 Primary scenarios

  • Given I'm an editor on a work, when I POST to /api/works/:id/categories with {name: "Frontend Frameworks"}, then the platform creates a category with id frontend-frameworks (auto-slugified) and commits the updated categories.yml to the data repo.
  • Given an item references category: frontend-frameworks and tags: [open-source, typescript], when I generate the website, then the rendered site shows the category and tags from categories.yml and tags.yml.
  • Given I rename a category, when the update completes, then the new name shows immediately and the slug id is preserved (so existing item references stay valid).
  • Given I'm a viewer, when I list categories/tags/collections, then I see them all; when I try to create or update one, then I get 403.

2.2 Edge cases & failures

  • Given I try to create a tag with the same name as an existing tag (case-insensitive match), when the validator runs, then the create is rejected with a clear "duplicate name" error before the YAML is touched.
  • Given I delete a category that items reference, when the delete completes, then the items keep their category field pointing at the now-deleted slug — they are NOT auto-reassigned; a follow-up cleanup is the user's responsibility.
  • Given I rename "My Category" to "Renamed Category", when the update commits, then the slug id stays my-category (slugs are immutable on update; renaming the slug requires delete + create).
  • Given the AI generates a new category during a pipeline run, when the categorization step finishes, then the new category is created via the same service path as a manual create (uniqueness validation, slug generation, git commit).

3. Functional Requirements

  • FR-1 The system MUST manage three taxonomy entity types: categories, tags, collections.
  • FR-2 Item cardinality MUST be: exactly 1 category (required), 0+ tags, 0–1 collection (optional).
  • FR-3 All entity ids MUST be auto-generated by slugifying the name with slugifyText() (URL-friendly, lowercase, hyphenated).
  • FR-4 Users MUST NOT be able to set ids directly.
  • FR-5 Slug ids MUST be immutable on update; renaming the entity keeps the existing id.
  • FR-6 Name uniqueness MUST be enforced case-insensitively per entity type within a work.
  • FR-7 Categories MUST support: id, name, optional description, optional icon_url, optional priority.
  • FR-8 Tags MUST support: id, name (no description / icon / priority — kept minimal).
  • FR-9 Collections MUST support: id, name, optional description, optional icon_url, optional priority.
  • FR-10 Storage MUST be YAML files in the data repo: categories.yml, tags.yml, collections.yml.
  • FR-11 Items MUST reference taxonomy entities by slug id.
  • FR-12 Read operations MUST require viewer role; mutations MUST require editor role (enforced by WorkOwnershipService).
  • FR-13 Deletion MUST NOT auto-reassign referencing items — they're left dangling until the user re-categorises.
  • FR-14 AI-generated taxonomy entries MUST flow through the same service as manual entries (uniqueness + slug + git commit).

4. Non-Functional Requirements

  • Performance: each CRUD operation is one read-modify-write cycle plus a git commit + push (~500 ms–2 s typical).
  • Reliability: a failed git push leaves both DB and repo unchanged.
  • Security & privacy: role-checked on every operation.
  • Observability: taxonomy mutations emit category_change, tag_change, collection_change entries in the work changelog.
  • Compatibility: YAML schema is backwards-compatible — older items missing optional fields still validate.

5. Key Entities & Domain Concepts

Entity / conceptDescription
CategoryPrimary classification; required on every item
TagDescriptive label; 0+ per item
CollectionEditorial grouping; 0–1 per item, optional
Slug idURL-friendly, lowercase, hyphen-separated; auto-generated from name
YAML filescategories.yml, tags.yml, collections.yml in the data repo
Reference styleItems reference taxonomy by slug id, not by name or numeric id

6. Out of Scope

  • Hierarchical / nested categories.
  • Tags with metadata (description, icon, priority).
  • Multiple collections per item.
  • Cross-work shared taxonomy.
  • Auto-reassignment on delete (deliberate choice; user owns cleanup).

7. Acceptance Criteria

  • Three CRUD surfaces with consistent slug + uniqueness behaviour.
  • Role-based access (viewer for read, editor for write).
  • Items reference by slug id; YAML round-trips cleanly.
  • AI generation goes through the same service.
  • Tests cover slug generation, duplicate detection, delete + referencing items, role enforcement.

8. Open Questions

None on develop.

9. Constitution Gates

  • I: AI generation uses the AI provider plugin (categorization step in the Standard Pipeline).
  • II: AI access via facade; role checks via the ownership service.
  • III: taxonomy lives in the data repo, not the database.
  • IV: AI generation runs inside existing pipeline; no new background work.
  • V: no DB schema (storage is in the repo).
  • VI: covered in packages/agent/src/services/__tests__/work-taxonomy.service.spec.ts.
  • VII: no secrets involved.
  • VIII: N/A.
  • IX: behaviour-first.
  • X: YAML schema is forward-compatible.

10. References

  • User-facing doc: ../../../features/taxonomy-system.md
  • Related: collections/spec.md
  • Implementation:
    • packages/agent/src/services/work-taxonomy.service.ts
    • packages/agent/src/services/work-ownership.service.ts
  • Pipeline: packages/plugins/standard-pipeline/src/steps/categories-tags-processing.step.ts