Mx Pg Controller MigrationSAFE
AI-powered CMS core for personal blogs and creator websites, with AI summaries, translation, moderation, and writing workflows.
Overview
AI-powered CMS core for personal blogs and creator websites, with AI summaries, translation, moderation, and writing workflows.
81b3f9a4c850OBSERVED · 2026-10-14What it tells the agent
The instruction file, verbatim from the audited commit — this is the text the model reads, and the surface the audit's instruction layer examines. Quoted here so you can judge it without cloning anything.
---
name: mx-pg-controller-migration
description: Use when verifying and porting an mx-core controller (Post/Note/Page/Comment/Category/etc.) after the MongoDB→PostgreSQL cutover, or when its data shape no longer matches what api-client and admin-vue3 expect. Triggers on "校验 controller"、"check controller"、"迁移 controller"、"修复迁移后的接口"、"data missing after PG migration"、"related/category 字段丢了" and similar.
---
# mx-core PG Cutover · Controller Verification & Downstream Sync
## Repos in scope (paths assume worktree root)
| Layer | Path | Concern |
| --- | --- | --- |
| Server | `apps/core/src/modules/<mod>/` | controller / service / repository correctness |
| SDK | `packages/api-client/{models,controllers}/` | type definitions must match server response |
| Dashboard | `/Users/innei/git/innei-repo/admin-vue3/apps/admin/src/{models,api,views/manage-<mod>}/` | consumer code reads new field names |
**No server-side back-compat shim.** If a field rename is correct on PG, propagate it through SDK and dashboard. The user has explicitly opted out of legacy aliases.
**Data completeness IS a bug.** A migration that compiles but silently drops `related`, `category`, or any joined value is broken. Always cross-check what the **old mongoose pipeline emitted** against what the new repository emits.
## Workflow (run in this order)
### 1 · Snapshot the migration delta
Identify the PG cutover commit for the module and diff against the last Mongo-era commit. The mx-core history convention:
```bash
# Last canonical pre-PG commit (refactor: comment module ...)
PRE_PG=58983aef
# PG cutover for content modules
PG_CUT=d5e582ba
git show $PRE_PG:apps/core/src/modules/<mod>/<mod>.controller.ts | head -200
git show $PRE_PG:apps/core/src/modules/<mod>/<mod>.model.ts
git log --oneline $PRE_PG..HEAD -- apps/core/src/modules/<mod>/
```
Look for: `autopopulate`, `aggregate(...$lookup, $project)`, `BaseModel`/`WriteBaseModel` virtuals, `count: { read, like }`, `pin`, `created`, `modified`. Any of these are likely lossy after migration.
### 2 · Walk every endpoint
Read the controller end to end. For each route, ask:
1. **Field names**: does the response shape still include what the dashboard / front-end consumes? (See mapping table below.)
2. **Joined data**: did mongoose emit a `category`, `related`, `topic`, `ref`, ... via `populate`/`$lookup`? If yes, does the new repository attach it? Use `attach<Foo>` helpers; never trust that the controller's `(doc as any).related` is populated — the repo is the source of truth.
3. **Aggregate-pipeline order**: old code often did `$project (select)` BEFORE `$lookup`, so `$lookup`-injected fields survived `select` filtering. The PG port frequently inverts that order and silently drops joined fields. **Check `select`-style projections in the controller** — they must whitelist or unconditionally preserve joined fields.
4. **Dead JSON-string parsing**: code like `if (typeof doc.meta === 'string') doc.meta = JSON.safeParse(...)` is dead under `jsonb`. Delete it.
5. **Redundant aliases**: `related: body.relatedId as any` style props that the service no longer reads. Delete.
6. **Cross-cutting enums** (`DraftRefType`, `CollectionRefTypes`, `CommentRefType`, `RecentlyRefTypes`): commit `b9823fb6` unified `ref_type` to **singular lowercase** (`'post' | 'note' | 'page' | 'recently'`). The PG SQL UPDATE migrated existing rows. The dashboard's local enum copies still hold legacy plural/PascalCase values and MUST be re-checked when verifying any module that touches drafts, comments, recently, file-references or ai-translations.
### 3 · Field rename map (Mongo → PG, after snake-case → camel-case round-trip)
| Mongo field | PG field | Notes |
| --- | --- | --- |
| `_id` | *(removed)* | Only `id` (Snowflake bigint as string) exists |
| `created` | `createdAt` | server returns `created_at`, SDK camelcases |
| `modified` | `modifiedAt` | nullable |
| `pin` (Date or null) | `pinAt` | nullable |
| `count: { read, like }` | `readCount`, `likeCount` | flat int columns |
| `commentsIndex`, `allowComment` | *(usually removed)* | check the PG schema; `posts/notes/pages` no longer have them, only `recentlies` does |
| populated `category` / `related` | computed via repo `attach*` | not in the row, must be loaded explicitly |
When in doubt, read `apps/core/src/database/schema/*.ts` — it is authoritative.
### 4 · Fix the server (apps/core)
Typical patches:
- **Repository**: extend `<Mod>Row` with optional joined fields (`category?`, `related?`); add a private `attach<Foo>(rows)` that does **one batched query**, never per-row N+1; wire it into `findById` / `findBySlug` / `find<...>` / `list`.
- **Controller**: when applying `select` whitelisting, force-include joined keys that are not addressable by the query string (`selected.add('id'); selected.add('category')`). Document why with a brief comment.
- **Service / controller**: drop redundant aliases; fold legacy input fields (`created`, `pin`) into their PG counterparts in the write path (the comment in `post.service.ts` after commit `536f1df9` is the reference pattern).
Run, scoped to the module:
```bash
pnpm -C apps/core exec tsc --noEmit
pnpm -C apps/core exec eslint src/modules/<mod>/
```
### 5 · Sync the SDK (packages/api-client)
The SDK type IS the contract. It must reflect the actual server payload after `snake_case → camelCase`.
For each renamed field:
1. Update `models/<mod>.ts` — usually means *not* extending the legacy `TextBaseModel` (which still has `created`/`modified`); flatten the model with PG names instead. Keep `BaseModel`/`TextBaseModel` untouched until the matching module is also being migrated, to avoid touching unrelated SDK types.
2. Grep for `Pick<<Mod>Model, ...>` across the SDK — `models/category.ts`, `models/aggregate.ts`, `controllers/<mod>.ts`, `controllers/search.ts` — and rename the picked keys.
3. Update `<Mod>ListOptions.sortBy` literal unions in `controllers/<mod>.ts` to the PG names.
```bash
pnpm -C pacTrust audit
SAFEgrade B · trust 89/100 Nothing in the source contradicts what it says it does. Grade A is reserved for packages that have also passed the behavioural sandbox.
| Layer | What it checks | Result |
|---|---|---|
| L0 | Provenance & inventory | WARN |
| L1 | Static analysis of the code | NA |
| L2 | Instruction surface (what it tells the agent) | PASS |
| L3 | Class-specific surface | PASS |
| L4 | Behavioural (sandbox) | SKIPPED |
What the source does
- Filesystem
- none-observed
- Network
- none-observed
- Shell
- none-observed
- Dependencies
- pinned
- Secrets in source
- none-found
Findings (1)
apps/core/.env
Gates applied: no_behavioural_pass.
81b3f9a4c850full audit observations/trust-audit/skill/mx-space__mx-pg-controller-migration.json · Report an issue / request a re-scanAudit history
Every audit this skill has had.
| Date | Source | Verdict | Grade | Score | Change |
|---|---|---|---|---|---|
| 2026-10-14 | 81b3f9a4c850 | SAFE | B | 89 | first audit |
Questions
What does the Mx Pg Controller Migration skill do?
AI-powered CMS core for personal blogs and creator websites, with AI summaries, translation, moderation, and writing workflows.
Is Mx Pg Controller Migration safe to install?
The audit found nothing in the source that contradicts what it says it does, and graded it B (89/100). Grade A is held back for packages that have also passed a sandboxed behavioural run, which is why a clean skill reads B.
What can Mx Pg Controller Migration access on my machine?
The audit observed no filesystem, network or shell use at all in its source.
How current is this page?
The grade is for one exact copy of the source (81b3f9a4c850), read on 2026-10-14. The repository is watched, and a new audit runs when it changes — this is the first audit.