Skip to content
Machine Learning
Skill

/moderator-page-migration

Port a moderator page from the main Next.js app (src/pages/moderator/**) into apps/moderator. Use when asked to migrate, move or cut over a /moderator/* page to the spoke, or to port its tRPC procedures and Prisma services to SvelteKit loads/actions and Kysely.

BOOST
From plugin
civitai
7.3k48 skills15 agents3 commands
Install
$ npx -y skills add civitai/civitai --skill moderator-page-migration --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/moderator-page-migration

Context preview

The summary Claude sees to decide when to auto-load this skill.

Port a moderator page from the main Next.js app (src/pages/moderator/**) into apps/moderator. Use when asked to migrate, move or cut over a /moderator/* page to the spoke, or to port its tRPC procedures and Prisma services to SvelteKit loads/actions and Kysely.

SKILL.md

moderator-page-migration.SKILL.md
name: moderator-page-migration
description: Port a moderator page from the main Next.js app (src/pages/moderator/**) into apps/moderator. Use when asked to migrate, move or cut over a /moderator/* page to the spoke, or to port its tRPC procedures and Prisma services to SvelteKit loads/actions and Kysely.

Main app → moderator spoke migration

Ports a page under `src/pages/moderator/**` into `apps/moderator`, then **switches the main-app page off**. The React component is not the work — the page's whole backend slice is. tRPC and Prisma do not exist in the spoke.

Sibling skill: [`retool-migration`](../retool-migration/SKILL.md), for the other inbound path. The two converge — same app, same conventions, same reviews — and differ only in what the source is and how you read it. Read that skill's §4–§6 if you have not built a page here before; they are not repeated.

Done means all three

A migration is one unit of work and it is not finished until every part of it is. Partial delivery is the failure mode this whole skill is arranged around — a page that is 90% ported and still live in both apps is worse than one that was never started, because a moderator now has two tools that disagree.

1. **Full functional parity.** Every query, every mutation, every side effect, and every action reachable from the page's child components. Not "the useful half" — §1 and §2 are how you enumerate it, §6.1 is the gate. 2. **The legacy page removed, and everything it orphans with it.** Delete the page file, redirect its URL, then trim the procedures, controllers, schemas and service functions nothing else uses. Leaving the old page live means moderators keep using it. Leaving dead procedures behind means the next reader cannot tell what is still load-bearing. §5. 3. **Reviewed** — `svelte-correctness-review`, `svelte-idiom-review` and `svelte-abstraction-review` over the segment, findings fixed. §6.

Do not tick the tracker until all three have happened.

0. Read the tracker first

[`docs/moderator-app/page-migration-checklist.md`](../../../docs/moderator-app/page-migration-checklist.md) is the tracker. Every page is a checkbox with its procedures, services, schemas and infra already enumerated, plus what earlier ports deferred. **Read your page's entry before writing code** — several carry a decision you would otherwise re-litigate (`/moderator/tags` may be superseded by `/images/tags`; Paddle pages are excluded by decision).

Update it as part of the work, not afterwards: tick the page, and write what you deliberately did not port. "Not ported" and "not needed" look identical six months later.

The second tracker is [`side-effect-parity-checklist.md`](../../../docs/moderator-app/side-effect-parity-checklist.md) — see §3.

1. Inventory the slice

A page is not its procedures. Walk outward until nothing new appears:

sed -n '1,80p' src/pages/moderator/<page>.tsx        # the flag guard and what it renders
grep -n "trpc\." src/pages/moderator/<page>.tsx      # procedures the PAGE calls
grep -rn "<procedure>" src/server/routers/           # → controller → service

Then, for every service function a mutation reaches, read it to the end. That is where the migration actually lives.

🔴 **The child components issue mutations the page never mentions.** A context menu, a row action, a confirm dialog — each imports `trpc` itself, so grepping the page file finds none of them. The `/moderator/articles` entry in the tracker says exactly this ("`ArticleContextMenu` may issue extra mutations — verify"), and it is the main-app analogue of the Retool export's missing event handlers. Grep the component tree, not the page:

grep -rn "trpc\.[a-z]" $(grep -o "from '~/components/[^']*'" src/pages/moderator/<page>.tsx \
  | sed "s|from '~/|src/|;s|'||") 2>/dev/null

**A shared service is ported once, for the whole cluster.** `image.service.ts` (~8K lines) and `report.service.ts` back most of the image/CSAM pages. Port the functions your page needs into a spoke service, and check `apps/moderator/src/lib/server/` first — 80+ services already exist there and the one you want is often among them.

2. Classify every mutation's side effects — before building

The queries port mechanically. **The mutations are the risk**, because a main-app moderation write is rarely one UPDATE: it fans out to notifications, email, Buzz, ClickHouse, Redis busts, search-index enqueue and session invalidation, and none of that is visible from the procedure name.

For each mutation, list every effect its service performs and put each into one bucket:

| Bucket | Meaning | | --- | --- | | **port** | The spoke does it. Most Postgres/Redis/ClickHouse/S3 work, via the `@civitai/*` packages. | | **delegate** | Stays in the main app behind an internal endpoint the spoke calls. The pattern exists: `syncKonoFinalize` in `apps/moderator/src/lib/server/kono.ts` → `src/pages/api/internal/kono-finalize.ts`. | | **defer** | Deliberately dropped for now. **Goes in the side-effect checklist with a severity**, not in a code comment. | | **equivalent** | Covered differently here — say how. |

An effect not in a bucket is dropped by accident, and a dropped side effect is invisible: the page works, the moderator sees success, and the notification/index/cache silently never happens. That is the whole reason `side-effect-parity-checklist.md` exists — it was written after an audit found eight of them across three already-migrated domains, two rated BLOCKER.

**Delegate rather than defer for anything a moderator would notice.** Deferring is for effects whose absence is recoverable (analytics, a self-healing metric); delegating is for the ones that are not.

3. Map main app → spoke

| Main app | Spoke | | --- | --- | | tRPC query procedure | `+page.server.ts` `load`, or an `+server.ts` endpoint if it is slow enough to keep off the load | | tRPC mutation | form `action` in `+page.server.ts`, calling a service | | `

Read more
Ships withcivitai

A repository of models, textual inversions, and more

Get the whole plugin

Other skills on civitai.