> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ooneex.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Topic Resource

> A ready-to-run spec that scaffolds the `topic` module as a flat, shared, admin-managed subject set, where only admins can mutate the vocabulary.

## The prompt

```yaml theme={null}
title: "Scaffold the topic module and its resources"
context: |
  Build a `topic` domain: subjects forming one shared vocabulary that every user
  reads but only admins curate. Topics may nest (parent + children) and record
  the owner who created them, but auth is role-based, not ownership-based: any
  authenticated user reads and lists; only an admin creates/updates/deletes.
  Slugs are unique. If `modules/topic/` already exists, this work is void — do not run.
goal: |
  Create the `topic` module + needed resources, wired admin-only on mutations.

  ## Notes
  - If `modules/topic/` exists, STOP and report. Else `/module:create` topic, then
    build each resource via its `*:create` skill (`--module=topic`), respecting
    controllers → services → repositories → entities, registering all.
  - Dependency — `user`. Resolve before the `/module:create` step above: if
    `modules/user/` is missing, create it FIRST from its prompt at
    https://docs.ooneex.com/ai/prompts/resources/user-resource.
  - Judge each resource; create the justified, skip the rest with a reason.
    Defaults: entity + repository always; service + controller per use case;
    permission always (admin-only guard on create/update/delete, read/list open
    to any authenticated user, reuse the permission service); seed if the project
    uses seeds (starter subject set); migration/event/translation/queue only if
    applicable; storage/workflow skip.
  - Enforce slug uniqueness in the service; throw a typed conflict (e.g.
    `TopicAlreadyExistsException`).
  - Throw typed exceptions (e.g. `TopicNotFoundException`), never return null.

  ### Data Model
  - `Topic.parent` ↔ `Topic.children` — self-referential ManyToOne / OneToMany
  - `Topic.owner` ↔ `User.topics` — ManyToOne / OneToMany
  - Links to topical entities live on the consuming module, not here.
dod: |
  - [ ] Aborts with a report if `modules/topic/` exists
  - [ ] Missing dependency module (`user`) created first from its docs prompt
  - [ ] `topic` module created and registered into the app and `SharedModule`
  - [ ] `Topic` entity with fields: `name`, `slug` (unique), `icon`, `color`
    (`SimpleColorType`), `position`, `description`, `lang` (`LocaleType`),
    `parent` (nullable), `children`, `owner`, `createdAt`, `updatedAt`
  - [ ] Full CRUD; any authenticated user reads/lists; only admin mutates;
    non-admin rejected on create/update/delete
  - [ ] Duplicate slug rejected
  - [ ] Unneeded resources skipped and reported with a reason
  - [ ] `bun run fmt`, `bun run lint`, `bun run test` pass from the root
```
