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.
- 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
- [ ] `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