title: "Scaffold the city module and its resources"
context: |
Build a `city` domain: a shared gazetteer of cities used as reference data
(addresses, venues, facets). Not user-owned — auth is role-based: any
authenticated user reads and lists; only an admin creates/updates/deletes.
Slugs are unique. A city points at a shared `Country` (country module) for the
country name and ISO codes, keeping only name/region/coordinates. If
`modules/city/` already exists, this work is void — do not run.
goal: |
Create the `city` module + needed resources, wired admin-only on mutations.
## Notes
- Run autonomously — do NOT ask the user questions. At every option or
judgment call, pick the recommended/default and proceed, reporting the
choices made in the final summary.
- If `modules/city/` exists, STOP and report. Else `/module:create` city, then
build each resource via its `*:create` skill (`--module=city`), respecting
controllers → services → repositories → entities, registering all.
- Destination — the `/module:create` step takes a `--destination` arg: pass
`app` when the project has a single `api` module; when it has several
`api`/`microservice` modules, infer the destination from this request.
- Dependency — `country`. Resolve before the `/module:create` step above: if
`modules/country/` is missing, create it FIRST from its prompt at
https://docs.talosjs.com/ai/prompts/resources/country-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 (base gazetteer); migration/event/translation only if applicable
(names are translation candidates); storage/queue/workflow skip.
- Wire the `Country` relation and reject a city whose `country` does not
resolve; do not copy country fields.
- Enforce slug uniqueness; throw a typed conflict (e.g. `CityAlreadyExistsException`).
- Throw typed exceptions (e.g. `CityNotFoundException`), never return null.
- Locating modules (e.g. `address`) reference this by relation, not copied fields.
### Data Model
- `City.country` ↔ `Country.cities` — ManyToOne / OneToMany
- `City.addresses` ↔ `Address.city` — OneToMany / ManyToOne (inverse on `address`)
dod: |
- [ ] Aborts with a report if `modules/city/` exists
- [ ] Missing dependency module (`country`) created first from its docs prompt
- [ ] `city` module created and registered into the app and `SharedModule`
- [ ] `City` entity with fields: `name`, `slug` (unique), `region`, `country`
(Country relation), `latitude`, `longitude`, `timezone` (IANA), `population`
(nullable), `lang` (`LocaleType`), `createdAt`, `updatedAt`
- [ ] Full CRUD; any authenticated user reads/lists; only admin mutates;
non-admin rejected on create/update/delete
- [ ] Duplicate slug rejected; references a `Country`, not a loose code;
unresolved `country` rejected
- [ ] If seeds are used, a base gazetteer is seeded
- [ ] Unneeded resources skipped and reported with a reason
- [ ] `bun run fmt`, `bun run lint`, `bun run test` pass from the root