Skip to content

Categories

One tree, shared by three kinds of content. Filing something under a category is the single act that decides what it is about, who may edit it, and what it is read with.

The tree

question_categories is self-referential — each row has a parent_id. A row also carries a system_id pointing at one of the 16 organ systems.

Three things are filed against it:

Content Column
A question question_category_id (primary), plus any number of additional links
An article category_id
A flashcard deck category_id

So one category filters all three, a grant over a branch reaches all three, and a deck generated from an article inherits the article's filing.

Primary and additional

A question has exactly one primary category and any number of additional ones, held as rows in a link table.

PATCH /questions/{id} takes additional_category_ids as the full replacement set — send the list you want to end up with, not a delta.

Filtering by a set of categories expands each to all its descendants first, then matches a question by its primary category or an additional link. So the filter is OR within categories and the branch is inclusive downward.

Tag filtering, by contrast, is AND: a question must carry every tag asked for.

Reassigning

Both PATCH /questions/{id} and the bulk action check the destination against assert_can_manage_category: it must be a category the caller holds.

  • A moderator holds every category, so a moderator may move anything anywhere.
  • A grant holder may move a question between their own branches, and is refused — 403 You do not have an editorial grant for this category — the moment the destination falls outside them. Setting the category to "uncategorized" is refused for the same reason: None is not in anybody's scope.

So a grant holder cannot move a question out of their own reach, which is the thing the restriction exists for.

Curating the tree

/categories is the Taxonomy screen, moderator-only. Two axes: Topics (the category tree) and Systems (the organ-system facet). Each row shows the questions filed directly in it and the total including everything beneath.

Act Endpoint Notes
Add POST /question-categories/ Name and a parent
Rename, or move under a different parent PATCH /question-categories/{id} A parent that would make a cycle is refused
Delete DELETE /question-categories/{id}?move_to= See below

Deleting asks two things first:

  1. Children must be moved first. "Move child categories before deleting this category."
  2. The questions have to go somewhere. If any question is filed there — by primary category or by an additional link — move_to is required. Without it: "N questions are filed here. Choose a topic to move them to before deleting this one." An empty topic goes without being asked.

Additional links follow the questions to the destination, minus any that would duplicate a pair the destination already has.

Deleting a category cascades

question_article_links, article_topic_claims and grants are all ON DELETE CASCADE. Deleting a category takes the reading links, the topic claims and anybody's grant over that branch with it. Check before you do it; see What can be undone.

Where it is curated

/categories — moderator-only. It is the administrative screen for the tree and the tag facets, not a reader-facing browse menu; the reader's category browser is the column view inside /articles.