@open-mercato/cli 0.5.1-develop.2972.6c5cd4a1c3 → 0.5.1-develop.2975.ccbadc8198

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -49,7 +49,11 @@ yarn db:generate # Generate migrations for all modules (writes to src/modules/
49
49
  yarn db:migrate # Apply all pending migrations (ordered, directory first)
50
50
  ```
51
51
 
52
- **Never hand-write migration files.** Update ORM entities in `data/entities.ts`, then run `yarn db:generate` to emit SQL and keep snapshots in sync.
52
+ Default workflow: update ORM entities in `data/entities.ts`, then run `yarn db:generate` to emit SQL and keep `.snapshot-open-mercato.json` in sync.
53
+
54
+ Coding-agent exception: if `yarn db:generate` emits unrelated migrations because another module's snapshot is stale, do not commit the noise. Delete unrelated generated files, keep or write only the SQL for the intended entity change, and update the affected module's `migrations/.snapshot-open-mercato.json` to the post-change schema. The snapshot update is mandatory; without it, standalone apps will regenerate already-committed migrations.
55
+
56
+ Do not run `yarn db:migrate` as part of generation unless the user explicitly asks to apply migrations. A PR should normally include the migration file plus snapshot, not depend on local DB state.
53
57
 
54
58
  ## Standalone App Considerations
55
59
 
@@ -41,7 +41,7 @@ step, you WILL produce incorrect imports and miss required patterns.
41
41
 
42
42
  | Task | Load |
43
43
  |---|---|
44
- | Add/modify an entity, create migration | `.ai/guides/core.md` → Module Files, then `yarn mercato db generate` |
44
+ | Add/modify an entity, create migration | `.ai/skills/data-model-design/SKILL.md`, `.ai/guides/core.md` → Module Files, then `yarn db:generate` |
45
45
  | Add a REST API endpoint | `.ai/guides/core.md` → API Routes |
46
46
  | Add a backend page | `.ai/guides/ui.md` → CrudForm / DataTable |
47
47
  | Configure sidebar navigation, page groups, settings pages | `.ai/skills/module-scaffold/references/navigation-patterns.md` |
@@ -135,11 +135,12 @@ Register in `src/modules.ts`: `{ id: '<id>', from: '@app' }`
135
135
 
136
136
  ## CRITICAL rules — always follow without exception
137
137
 
138
- 1. **After editing any entity file**: run `yarn mercato db generate` (never hand-write migrations)
138
+ 1. **Entity classes live in `src/modules/<module>/data/entities.ts` and MUST import decorators from `@mikro-orm/decorators/legacy`.** Start there for every schema change.
139
139
  2. **After editing `src/modules.ts`** or any structural module file: run `yarn generate`
140
140
  3. **Never edit `.mercato/generated/*`** — auto-generated. Never edit `node_modules/@open-mercato/*` — eject instead.
141
- 4. **Confirm migrations with user** before running `yarn mercato db migrate`
142
- 5. **API route files MUST export `metadata`.** Every `src/modules/<module>/api/**/route.ts` that exports an HTTP handler (`GET`, `POST`, `PUT`, `PATCH`, `DELETE`) MUST also export a `metadata` object describing per-method auth, otherwise the generator emits the warning `[generate] ⚠ Route file exports handlers but no metadata — auth will default to required` and every method silently falls back to "authentication required". Use the per-method shape:
141
+ 4. **After editing `src/modules/<module>/data/entities.ts`**: run `yarn db:generate` as a schema-diff probe. Default to the generated SQL, but if it emits unrelated churn, keep or write only the scoped SQL for your module and update `src/modules/<module>/migrations/.snapshot-open-mercato.json` in the same change.
142
+ 5. **Confirm migrations with user** before running `yarn db:migrate`
143
+ 6. **API route files MUST export `metadata`.** Every `src/modules/<module>/api/**/route.ts` that exports an HTTP handler (`GET`, `POST`, `PUT`, `PATCH`, `DELETE`) MUST also export a `metadata` object describing per-method auth, otherwise the generator emits the warning `[generate] ⚠ Route file exports handlers but no metadata — auth will default to required` and every method silently falls back to "authentication required". Use the per-method shape:
143
144
  ```ts
144
145
  export const metadata = {
145
146
  GET: { requireAuth: true, requireFeatures: ['mymodule.view'] },
@@ -147,13 +148,13 @@ Register in `src/modules.ts`: `{ id: '<id>', from: '@app' }`
147
148
  }
148
149
  ```
149
150
  For public endpoints, opt out explicitly with `{ requireAuth: false }`. Do not use the legacy top-level `export const requireAuth` / `export const requireFeatures` — they are no longer recognised.
150
- 6. **Write migrations in one shot.** `yarn dev` auto-applies pending migrations at startup by default (`OM_DEV_AUTO_MIGRATE=1`). Once a generated migration has been run, editing the same migration file has no effect — the next migrate pass skips it as already-applied. If you truly need to iterate, either (a) disable auto-migrate (`OM_DEV_AUTO_MIGRATE=0`) and roll back with `yarn db:migrate --down` between edits, or (b) ship the correction as a **new** migration file. Never hand-edit a historical migration that has already been applied in any environment.
151
- 7. **After the user adds a new module, offer to trim classic mode.** A fresh `create-mercato-app` scaffold enables every built-in module (classic mode). Once the user has added their own custom module, the defaults are usually dead weight. **Ask the user** (via a short `AskUserQuestion`) whether they want to disable built-in modules that are not needed for their project. If they say yes, invoke the `trim-unused-modules` skill — do NOT hand-craft the slimdown inside the AGENTS.md reading flow. If they say no, preserve classic mode silently.
151
+ 7. **Write migrations in one shot.** `yarn dev` auto-applies pending migrations at startup by default (`OM_DEV_AUTO_MIGRATE=1`). Once a migration has been applied, editing the same file usually has no effect — the next migrate pass skips it as already-applied. If `yarn db:generate` shows unrelated churn, manual SQL for the intended module is allowed, but you MUST also update that module's `.snapshot-open-mercato.json`. Never hand-edit a historical migration that has already shipped; add a **new** migration instead.
152
+ 8. **After the user adds a new module, offer to trim classic mode.** A fresh `create-mercato-app` scaffold enables every built-in module (classic mode). Once the user has added their own custom module, the defaults are usually dead weight. **Ask the user** (via a short `AskUserQuestion`) whether they want to disable built-in modules that are not needed for their project. If they say yes, invoke the `trim-unused-modules` skill — do NOT hand-craft the slimdown inside the AGENTS.md reading flow. If they say no, preserve classic mode silently.
152
153
 
153
154
  **Dashboards fallback rule.** When the user (or the `trim-unused-modules` skill) disables the `dashboards` module, you MUST update `src/app/(backend)/backend/page.tsx` so it no longer renders `<DashboardScreen />`. Replace the dashboard render with a `redirect(...)` to the first enabled backend page for the current user — preferring pages already registered in the main sidebar group and respecting the admin/superadmin role of the caller. Otherwise `/backend` will crash at build or request time because the removed module no longer ships `DashboardScreen`. Always fall back to `/backend/profile` only if no other backend page is available.
154
- 8. **New features MUST be visible to admin/superadmin immediately.** Every time you add a new feature ID (e.g. `my_module.view`, `my_module.manage`) to `src/modules/<module>/acl.ts`, you MUST also (a) add that feature to `defaultRoleFeatures` in the same module's `setup.ts` so the admin and superadmin roles get it on every tenant setup; and (b) run `yarn mercato auth sync-role-acls --all-tenants` so existing tenants pick up the new feature without a reinstall. Do this automatically unless the user has explicitly said otherwise — the user should see the features you are building, not stare at a blank admin because their role is missing a grant. Feature IDs are FROZEN once shipped; if a rename is required, add the new ID alongside, grant it, and keep the old one as a deprecated alias.
155
- 9. **Strict Design System alignment for every UI change.** Any UI you add or edit MUST use the Open Mercato design system components and tokens. No hardcoded Tailwind status colors (`text-red-500`, `bg-green-100`, etc.) — use semantic tokens (`text-status-error-text`, `bg-status-success-bg`, …). No arbitrary text sizes (`text-[11px]`, `text-[13px]`) — use the Tailwind scale (`text-xs`, `text-sm`, `text-base`, `text-lg`, `text-xl`, `text-2xl`) or the `text-overline` token for 11px uppercase labels. In PAGE BODY UI, use `lucide-react` icons (never inline `<svg>`); but in `page.meta.ts` files specifically, the `icon` field MUST be built with `React.createElement('svg', { width: 16, height: 16, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2 }, React.createElement('path', { d: '...' }))` — see the reference pattern in `node_modules/@open-mercato/core/dist/modules/customers/backend/customers/people/page.meta.js` or mirror `packages/core/src/modules/customers/backend/customers/people/page.meta.ts` in the monorepo. Do NOT `import { IconName } from 'lucide-react'` inside meta files — after the lucide-react major upgrade, direct imports in meta files break page-metadata serialization. Grab the `d="..."` from the lucide icon you want and inline it via `React.createElement`. Use `StatusBadge` for entity status, `Alert` for inline feedback, `FormField` for standalone form inputs, `SectionHeader` for detail-page section headings, `CollapsibleSection` for collapsible regions, `LoadingMessage`/`Spinner`/`DataLoader` for async states, and `EmptyState` (or DataTable's `emptyState` prop) for empty lists. Every dialog MUST support `Cmd/Ctrl+Enter` to submit and `Escape` to cancel. Every icon-only button MUST have an `aria-label`. These rules apply to `src/modules/<module>/backend/**` and `src/modules/<module>/frontend/**` alike.
156
- 7. **BEFORE writing ANY code**, you MUST:
155
+ 9. **New features MUST be visible to admin/superadmin immediately.** Every time you add a new feature ID (e.g. `my_module.view`, `my_module.manage`) to `src/modules/<module>/acl.ts`, you MUST also (a) add that feature to `defaultRoleFeatures` in the same module's `setup.ts` so the admin and superadmin roles get it on every tenant setup; and (b) run `yarn mercato auth sync-role-acls --all-tenants` so existing tenants pick up the new feature without a reinstall. Do this automatically unless the user has explicitly said otherwise — the user should see the features you are building, not stare at a blank admin because their role is missing a grant. Feature IDs are FROZEN once shipped; if a rename is required, add the new ID alongside, grant it, and keep the old one as a deprecated alias.
156
+ 10. **Strict Design System alignment for every UI change.** Any UI you add or edit MUST use the Open Mercato design system components and tokens. No hardcoded Tailwind status colors (`text-red-500`, `bg-green-100`, etc.) — use semantic tokens (`text-status-error-text`, `bg-status-success-bg`, …). No arbitrary text sizes (`text-[11px]`, `text-[13px]`) — use the Tailwind scale (`text-xs`, `text-sm`, `text-base`, `text-lg`, `text-xl`, `text-2xl`) or the `text-overline` token for 11px uppercase labels. In PAGE BODY UI, use `lucide-react` icons (never inline `<svg>`). Use `StatusBadge` for entity status, `Alert` for inline feedback, `FormField` for standalone form inputs, `SectionHeader` for detail-page section headings, `CollapsibleSection` for collapsible regions, `LoadingMessage`/`Spinner`/`DataLoader` for async states, and `EmptyState` (or DataTable's `emptyState` prop) for empty lists. For list pages, follow `.ai/skills/backend-ui-design/SKILL.md` and prefer the `DataTable` host pattern shown there (`entityId`, `apiPath`, stable `extensionTableId`, and explicit pagination props when you own the data source). Every dialog MUST support `Cmd/Ctrl+Enter` to submit and `Escape` to cancel. Every icon-only button MUST have an `aria-label`. These rules apply to `src/modules/<module>/backend/**` and `src/modules/<module>/frontend/**` alike.
157
+ 11. **BEFORE writing ANY code**, you MUST:
157
158
  - Match your task against the **Task → Context Map** above
158
159
  - `Read` every file listed in the "Load" column for your task type
159
160
  - Only then proceed to implementation
@@ -163,6 +164,7 @@ Register in `src/modules.ts`: `{ id: '<id>', from: '@app' }`
163
164
  ## Additional Conventions
164
165
 
165
166
  - Custom modules use `from: '@app'` in `src/modules.ts`
167
+ - Entity classes belong in `src/modules/<module>/data/entities.ts` and use decorators from `@mikro-orm/decorators/legacy`
166
168
  - Standalone apps expose `yarn mercato configs cache ...` because the template enables the `configs` module from `@open-mercato/core`
167
169
  - `yarn generate` automatically runs a best-effort structural cache purge (`yarn mercato configs cache structural --all-tenants`) after successful generation; if the cache command is unavailable, generation still succeeds
168
170
  - Detail/read-model APIs that expose `customFields` MUST return bare field keys via `normalizeCustomFieldResponse()` (for example `{ priority: 3 }`). Keep `cf_` / `cf:` prefixes for request payloads, filters, and form field IDs only.
@@ -170,7 +172,7 @@ Register in `src/modules.ts`: `{ id: '<id>', from: '@app' }`
170
172
  - `page.meta.ts` MUST include `pageGroup`, `pageGroupKey`, and `pageOrder` for sidebar grouping
171
173
  - Settings pages MUST use `pageContext: 'settings' as const` with `navHidden: true`
172
174
  - All related pages within a module MUST share the same `pageGroupKey`
173
- - DataTable MUST wire pagination props (`page`, `pageSize`, `totalCount`, `onPageChange`)
175
+ - DataTable hosts MUST keep `extensionTableId` stable; when you own pagination state, wire `page`, `pageSize`, `totalCount`, and `onPageChange`
174
176
 
175
177
  ## Naming Conventions
176
178
 
@@ -227,8 +229,8 @@ import type { ApiInterceptor } from '@open-mercato/shared/lib/crud/api-intercept
227
229
  | `yarn generate` | Regenerate `.mercato/generated/` |
228
230
  | `yarn mercato configs cache structural --all-tenants` | Manually purge structural navigation/sidebar cache entries |
229
231
  | `yarn mercato module add <package>` | Install and enable an official module package |
230
- | `yarn mercato db generate` | Create migration for entity changes |
231
- | `yarn mercato db migrate` | Apply pending migrations |
232
+ | `yarn db:generate` | Probe/create migration SQL for entity changes |
233
+ | `yarn db:migrate` | Apply pending migrations after user confirmation |
232
234
  | `yarn initialize` | Bootstrap DB + first admin account |
233
235
  | `yarn build` | Build for production |
234
236
  | `yarn mercato eject <module>` | Copy a core module into `src/modules/` |
@@ -53,7 +53,7 @@ gh label create in-progress --color c5def5 --description "Auto-skill is wo
53
53
 
54
54
  ## 3. Validation gate probes `package.json` scripts
55
55
 
56
- SKILL.md lists commands like `yarn typecheck`, `yarn test`, `yarn generate`, `yarn build:packages`, `yarn build:app`, `yarn i18n:check-sync`, `yarn i18n:check-usage`. Standalone apps typically have `yarn typecheck`, `yarn test`, `yarn generate`, `yarn build`, but not the monorepo-specific `:packages` / `:app` splits.
56
+ SKILL.md lists commands like `yarn typecheck`, `yarn test`, `yarn generate`, `yarn build:packages`, `yarn build:app`, `yarn i18n:check-sync`, `yarn i18n:check-usage`. The current standalone template ships `yarn build`, `yarn typecheck`, `yarn test`, `yarn generate`, `yarn db:generate`, and `yarn db:migrate`; monorepo-specific `build:packages`, `build:app`, and `i18n:*` scripts usually do not exist.
57
57
 
58
58
  Before running each step, probe:
59
59
 
@@ -53,7 +53,7 @@ gh label create in-progress --color c5def5 --description "Auto-skill is wo
53
53
 
54
54
  ## 3. Validation gate probes `package.json` scripts
55
55
 
56
- SKILL.md lists commands like `yarn typecheck`, `yarn test`, `yarn generate`, `yarn build:packages`, `yarn build:app`, `yarn i18n:check-sync`, `yarn i18n:check-usage`. Standalone apps typically have `yarn typecheck`, `yarn test`, `yarn generate`, `yarn build`, but not the monorepo-specific `:packages` / `:app` splits.
56
+ SKILL.md lists commands like `yarn typecheck`, `yarn test`, `yarn generate`, `yarn build:packages`, `yarn build:app`, `yarn i18n:check-sync`, `yarn i18n:check-usage`. The current standalone template ships `yarn build`, `yarn typecheck`, `yarn test`, `yarn generate`, `yarn db:generate`, and `yarn db:migrate`; monorepo-specific `build:packages`, `build:app`, and `i18n:*` scripts usually do not exist.
57
57
 
58
58
  Before running each step, probe:
59
59
 
@@ -53,7 +53,7 @@ gh label create in-progress --color c5def5 --description "Auto-skill is wo
53
53
 
54
54
  ## 3. Validation gate probes `package.json` scripts
55
55
 
56
- SKILL.md lists commands like `yarn typecheck`, `yarn test`, `yarn generate`, `yarn build:packages`, `yarn build:app`, `yarn i18n:check-sync`, `yarn i18n:check-usage`. Standalone apps typically have `yarn typecheck`, `yarn test`, `yarn generate`, `yarn build`, but not the monorepo-specific `:packages` / `:app` splits.
56
+ SKILL.md lists commands like `yarn typecheck`, `yarn test`, `yarn generate`, `yarn build:packages`, `yarn build:app`, `yarn i18n:check-sync`, `yarn i18n:check-usage`. The current standalone template ships `yarn build`, `yarn typecheck`, `yarn test`, `yarn generate`, `yarn db:generate`, and `yarn db:migrate`; monorepo-specific `build:packages`, `build:app`, and `i18n:*` scripts usually do not exist.
57
57
 
58
58
  Before running each step, probe:
59
59
 
@@ -282,8 +282,8 @@ Record findings from the patterns below. These are mandatory findings, not optio
282
282
  | Hardcoded user-facing string in API errors or UI labels | Medium: must use i18n |
283
283
  | New `any` type annotation outside tests | Medium: use zod plus `z.infer` |
284
284
  | `alert(` or custom toast instead of `flash()` | Medium: use `flash()` |
285
- | Hand-written migration SQL file | Medium: never hand-write migrations |
286
- | Entity schema changed but no migration file in the diff | Medium: run `yarn db:generate` |
285
+ | Hand-written migration SQL file without snapshot update or scope rationale | Medium: prefer generated migrations; manual SQL must be scoped and update `.snapshot-open-mercato.json` |
286
+ | Entity schema changed but no migration file or no-op rationale in the diff | Medium: create a scoped migration and update the snapshot |
287
287
  | Missing explicit tenant scoping in sub-entity queries | Medium: defense in depth |
288
288
  | New or modified i18n locale JSON keys not in alphabetical order | Medium: CI i18n-check-sync requires sorted keys — run `yarn i18n:check-sync --fix` or sort manually |
289
289
 
@@ -53,7 +53,7 @@ gh label create in-progress --color c5def5 --description "Auto-skill is wo
53
53
 
54
54
  ## 3. Validation gate probes `package.json` scripts
55
55
 
56
- SKILL.md lists commands like `yarn typecheck`, `yarn test`, `yarn generate`, `yarn build:packages`, `yarn build:app`, `yarn i18n:check-sync`, `yarn i18n:check-usage`. Standalone apps typically have `yarn typecheck`, `yarn test`, `yarn generate`, `yarn build`, but not the monorepo-specific `:packages` / `:app` splits.
56
+ SKILL.md lists commands like `yarn typecheck`, `yarn test`, `yarn generate`, `yarn build:packages`, `yarn build:app`, `yarn i18n:check-sync`, `yarn i18n:check-usage`. The current standalone template ships `yarn build`, `yarn typecheck`, `yarn test`, `yarn generate`, `yarn db:generate`, and `yarn db:migrate`; monorepo-specific `build:packages`, `build:app`, and `i18n:*` scripts usually do not exist.
57
57
 
58
58
  Before running each step, probe:
59
59
 
@@ -7,7 +7,7 @@ description: Design and implement consistent backend/backoffice interfaces using
7
7
 
8
8
  Guide for creating consistent, production-grade backend interfaces using the `@open-mercato/ui` component library. All implementations must use existing components for visual and behavioral consistency.
9
9
 
10
- For complete component API reference, see `references/ui-components.md`.
10
+ For complete component API reference, see `references/ui-components.md`. Pair this skill with `.ai/guides/ui.md` when present and with the standalone `AGENTS.md` rules for DataTable hosts, design-system primitives, and backend page conventions.
11
11
 
12
12
  ## Design Principles
13
13
 
@@ -16,6 +16,7 @@ For complete component API reference, see `references/ui-components.md`.
16
16
  3. **Data Density**: Admin users need information-rich interfaces. Optimize for scanning.
17
17
  4. **Keyboard Navigation**: `Cmd/Ctrl+Enter` for primary actions, `Escape` to cancel.
18
18
  5. **Clear Hierarchy**: Page → Section → Content. Use `PageHeader`, `PageBody`, consistent spacing.
19
+ 6. **Design System Discipline**: Use semantic status tokens plus shared primitives like `StatusBadge`, `Alert`, `FormField`, `SectionHeader`, `CollapsibleSection`, and `EmptyState`. No hardcoded status colors or arbitrary text sizes.
19
20
 
20
21
  ## Required Component Library
21
22
 
@@ -50,6 +51,26 @@ Column patterns:
50
51
  - Status/enum: `EnumBadge` with severity presets
51
52
  - Actions: `RowActions` for context menus
52
53
 
54
+ ### Preferred DataTable Host Pattern
55
+
56
+ For standard CRUD lists, prefer the built-in host pattern instead of manually fetching and shaping rows:
57
+
58
+ ```tsx
59
+ <DataTable
60
+ entityId="tickets.ticket"
61
+ apiPath="tickets/tickets"
62
+ extensionTableId="tickets.ticket"
63
+ columns={columns}
64
+ createHref="/backend/tickets/tickets/new"
65
+ emptyState={{
66
+ title: t('tickets.list.empty.title'),
67
+ description: t('tickets.list.empty.description'),
68
+ }}
69
+ />
70
+ ```
71
+
72
+ Keep `extensionTableId` stable so DataTable injections remain backward-compatible.
73
+
53
74
  ### DataTable Pagination
54
75
 
55
76
  DataTable MUST be configured with pagination props to display all data correctly. Without these, the table only shows the first page with no way to navigate:
@@ -182,6 +203,8 @@ import { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customF
182
203
  - [ ] Loading states use `LoadingMessage` or `DataLoader`
183
204
  - [ ] Error states use `ErrorMessage`, `ErrorNotice`, or `Notice variant="error"`
184
205
  - [ ] Empty states use `EmptyState`
206
+ - [ ] Status displays use `StatusBadge` or `EnumBadge`, not hardcoded colors
207
+ - [ ] Standalone inputs use `FormField`; detail sections use `SectionHeader` / `CollapsibleSection` when applicable
185
208
  - [ ] Column truncation uses `meta.truncate` and `meta.maxWidth`
186
209
  - [ ] Boolean values use `BooleanIcon`
187
210
  - [ ] Status/enum values use `EnumBadge`
@@ -194,7 +217,7 @@ import { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customF
194
217
  2. Manual table markup — use `DataTable`
195
218
  3. Custom toast/notification — use `flash()`
196
219
  4. Inline styles — use Tailwind classes
197
- 5. Hardcoded colors — use theme variables
220
+ 5. Hardcoded colors or status classes — use theme variables and semantic status tokens
198
221
  6. Missing loading states — every async operation needs feedback
199
222
  7. Missing error handling — every failure needs messaging
200
223
  8. Missing keyboard shortcuts — all dialogs need `Cmd+Enter` and `Escape`
@@ -47,7 +47,7 @@ Complete reference of all available UI components in `@open-mercato/ui`.
47
47
 
48
48
  | Component | Import Path | Purpose | Key Props |
49
49
  |-----------|-------------|---------|-----------|
50
- | **DataTable** | `@open-mercato/ui/backend/DataTable` | Feature-rich table with sorting, filtering, pagination, export, perspectives | `columns`, `data`, `filters`, `pagination`, `perspective`, `onRowClick` |
50
+ | **DataTable** | `@open-mercato/ui/backend/DataTable` | Feature-rich table with sorting, filtering, pagination, export, perspectives | `entityId`, `apiPath`, `extensionTableId`, `columns`, `data`, `page`, `pageSize`, `totalCount`, `onPageChange`, `onRowClick` |
51
51
  | **TruncatedCell** | `@open-mercato/ui/backend/TruncatedCell` | Table cell with text truncation and tooltip | `value`, `maxWidth` |
52
52
  | **EmptyState** | `@open-mercato/ui/backend/EmptyState` | Empty state placeholder | `title`, `description`, `action`, `icon` |
53
53
  | **RowActions** | `@open-mercato/ui/backend/RowActions` | Context menu for row actions | `items: {label, href?, onSelect?, destructive?}[]` |
@@ -127,14 +127,18 @@ const filters: FilterDef[] = [
127
127
  ]
128
128
 
129
129
  <DataTable
130
- title="Items"
130
+ entityId="inventory.item"
131
+ apiPath="inventory/items"
132
+ extensionTableId="inventory.item"
131
133
  columns={columns}
132
- data={data}
133
134
  filters={filters}
134
135
  filterValues={filterValues}
135
136
  onFiltersApply={handleFiltersApply}
136
137
  onFiltersClear={handleFiltersClear}
137
- pagination={{ page, pageSize, total, totalPages, onPageChange }}
138
+ page={page}
139
+ pageSize={pageSize}
140
+ totalCount={totalCount}
141
+ onPageChange={setPage}
138
142
  onRowClick={(row) => router.push(`/items/${row.id}`)}
139
143
  />
140
144
  ```
@@ -228,6 +232,7 @@ const deleted = await deleteCrud('module/items', id)
228
232
  1. **Always use `flash()` for notifications** - Don't use `alert()` or custom toast implementations
229
233
  2. **Use `CrudForm` for forms** - Provides consistent validation, field rendering, and keyboard shortcuts
230
234
  3. **Use `DataTable` for lists** - Includes filtering, sorting, pagination, export, and perspectives
231
- 4. **Use `JsonBuilder` for JSON editing** - Provides both raw JSON and visual builder modes
232
- 5. **Dialog forms need `embedded={true}`** - And add `[&_.grid]:!grid-cols-1` to DialogContent for single-column layout
233
- 6. **Support Cmd/Ctrl+Enter and Escape** - All dialogs should support these keyboard shortcuts
235
+ 4. **Keep `extensionTableId` stable** - Injection spots must remain backward-compatible
236
+ 5. **Use `JsonBuilder` for JSON editing** - Provides both raw JSON and visual builder modes
237
+ 6. **Dialog forms need `embedded={true}`** - And add `[&_.grid]:!grid-cols-1` to DialogContent for single-column layout
238
+ 7. **Support Cmd/Ctrl+Enter and Escape** - All dialogs should support these keyboard shortcuts
@@ -98,7 +98,7 @@ Omit empty severity sections.
98
98
 
99
99
  ### Data Integrity
100
100
 
101
- - **Never hand-write migrations** — update entities, run `yarn db:generate`
101
+ - **Migration files and snapshots must match entity intent** — prefer `yarn db:generate`; scoped manual SQL is allowed only to avoid unrelated generated churn and must include `.snapshot-open-mercato.json`
102
102
  - **Validate migration scope** — autogenerated doesn't mean correct
103
103
  - **Workers/subscribers MUST be idempotent**
104
104
  - **Commands MUST be undoable** — include before/after snapshots
@@ -130,8 +130,8 @@ Omit empty severity sections.
130
130
  ## Review Heuristics
131
131
 
132
132
  1. **New files**: Check if `yarn generate` is needed. Verify auto-discovery paths.
133
- 2. **Entity changes**: Check if `yarn db:generate` is needed. Look for missing tenant columns.
134
- 3. **Migration sanity**: Inspect SQL content. Reject unrelated schema churn.
133
+ 2. **Entity changes**: Check if migration and snapshot updates are needed. Look for missing tenant columns.
134
+ 3. **Migration sanity**: Inspect SQL content and `.snapshot-open-mercato.json`. Reject unrelated schema churn.
135
135
  4. **New API routes**: Verify `openApi` export, auth guards, zod validation, tenant filtering.
136
136
  5. **Event emitters**: Verify event is declared in `events.ts` with `as const`.
137
137
  6. **Commands**: Verify undoable, before/after snapshots.
@@ -20,7 +20,7 @@
20
20
 
21
21
  ## 3. Data Integrity & ORM
22
22
 
23
- - [ ] No hand-written migrationsentities updated, `yarn db:generate` run
23
+ - [ ] Migration workflow is coherent — `yarn db:generate` was used as the default probe, or scoped manual SQL is justified and paired with a matching `.snapshot-open-mercato.json` update
24
24
  - [ ] Migration scope matches PR intent (no unrelated schema churn)
25
25
  - [ ] UUID primary keys with standard columns (`id`, `created_at`, `updated_at`)
26
26
  - [ ] Soft delete via `deleted_at` where applicable
@@ -37,8 +37,10 @@ When the developer describes data requirements:
37
37
 
38
38
  ### Standard Entity Template
39
39
 
40
+ Define entities in `src/modules/<module_id>/data/entities.ts`. Standalone apps keep the module's entity classes together there unless the file becomes large enough that a split is justified.
41
+
40
42
  ```typescript
41
- import { Entity, Property, PrimaryKey, Index, Enum } from '@mikro-orm/core'
43
+ import { Entity, Enum, Index, PrimaryKey, Property } from '@mikro-orm/decorators/legacy'
42
44
  import { v4 } from 'uuid'
43
45
 
44
46
  @Entity({ tableName: '<entities>' })
@@ -322,14 +324,15 @@ const enricher: ResponseEnricher = {
322
324
  ### Creating a Migration
323
325
 
324
326
  ```bash
325
- # 1. Modify or create entity files
326
- # 2. Generate migration
327
+ # 1. Modify src/modules/<module_id>/data/entities.ts
328
+ # 2. Probe/generate migration
327
329
  yarn db:generate
328
330
 
329
- # 3. Review the generated migration
331
+ # 3. Review the generated migration or use it as the baseline for scoped manual SQL
330
332
  # Check src/modules/<module_id>/migrations/Migration_YYYYMMDD_HHMMSS.ts
331
333
 
332
- # 4. Apply migration (confirm with user first)
334
+ # 4. Update src/modules/<module_id>/migrations/.snapshot-open-mercato.json
335
+ # 5. Apply migration only after explicit user confirmation
333
336
  yarn db:migrate
334
337
  ```
335
338
 
@@ -337,9 +340,12 @@ yarn db:migrate
337
340
 
338
341
  1. **Review every migration** — auto-generated doesn't mean correct
339
342
  2. **Check for unintended changes** — sometimes generators pick up unrelated diffs
340
- 3. **New columns should have defaults** — prevents breaking existing rows
341
- 4. **Never rename columns** add new column, migrate data, remove old column (across releases)
342
- 5. **Never drop tables** soft delete or archive first
343
+ 3. **Do not commit unrelated generated migrations** — delete them from the diff
344
+ 4. **Scoped manual SQL is allowed** when generator churn is unrelated, but the migration and `.snapshot-open-mercato.json` must still describe the same post-change schema
345
+ 5. **Update `.snapshot-open-mercato.json`**it is the baseline that prevents duplicate future migrations
346
+ 6. **New columns should have defaults** — prevents breaking existing rows
347
+ 7. **Never rename columns** — add new column, migrate data, remove old column (across releases)
348
+ 8. **Never drop tables** — soft delete or archive first
343
349
 
344
350
  ### Adding a Column to Existing Entity
345
351
 
@@ -355,8 +361,8 @@ new_field: string | null = null
355
361
 
356
362
  Then:
357
363
  ```bash
358
- yarn db:generate # Creates ALTER TABLE ADD COLUMN migration
359
- yarn db:migrate # Applies it
364
+ yarn db:generate # Probes/creates ALTER TABLE ADD COLUMN migration
365
+ yarn db:migrate # Applies it only after explicit user confirmation
360
366
  ```
361
367
 
362
368
  ### Removing a Column
@@ -484,7 +490,8 @@ export class TicketHistory {
484
490
  | `@ManyToOne` across modules | Tight coupling, breaks module isolation | Store FK as `uuid` column, use enrichers |
485
491
  | Storing computed values | Stale data, maintenance burden | Compute on read via enrichers or queries |
486
492
  | Using `any` for JSONB fields | No type safety | Define a Zod schema, use `z.infer` |
487
- | Manual migration SQL | Fragile, version-dependent | Use `yarn db:generate` |
493
+ | Blindly committing all generated migrations | Captures unrelated snapshot drift | Keep only scoped SQL and update the matching snapshot |
494
+ | Manual migration SQL without snapshot update | Future `yarn db:generate` recreates the same migration | Update `.snapshot-open-mercato.json` in the same change |
488
495
  | Renaming columns | Breaks existing data/queries | Add new column, migrate data, drop old |
489
496
  | Missing `organization_id` | Cross-tenant data leaks | Always include and index |
490
497
  | Using `varchar` without `length` | Defaults vary by DB | Always specify `length` |
@@ -500,8 +507,10 @@ export class TicketHistory {
500
507
  - **MUST** include standard columns (`id`, `created_at`, `updated_at`, `deleted_at`, `is_active`)
501
508
  - **MUST** use UUID v4 for primary keys
502
509
  - **MUST** index all FK columns and `organization_id` / `tenant_id`
503
- - **MUST** run `yarn db:generate` after entity changes, never hand-write migrations
510
+ - **MUST** create or keep a scoped migration after entity changes and update `.snapshot-open-mercato.json`
504
511
  - **MUST** review generated migration before applying
512
+ - **MUST NOT** commit unrelated migrations emitted by `yarn db:generate`
513
+ - **MUST NOT** run `yarn db:migrate` without explicit user confirmation
505
514
  - **MUST** use `nullable: true` with `= null` default for optional fields
506
515
  - **MUST** specify `length` on all `varchar` columns
507
516
  - **MUST NOT** use ORM relationship decorators across module boundaries
@@ -129,7 +129,7 @@ After all targeted phases are complete:
129
129
  3. **Build check**: `yarn build` — must pass
130
130
  4. **Unit test check**: `yarn test` — must pass
131
131
  5. **Integration test check**: run any new integration tests — must pass
132
- 6. **Migration check**: `yarn mercato db generate` — if any entities changed (verify generated migration is scoped correctly)
132
+ 6. **Migration check**: `yarn db:generate` — if any entities changed (verify the resulting SQL is scoped correctly; manual SQL is acceptable only when avoiding unrelated churn, and the touched `.snapshot-open-mercato.json` must match)
133
133
 
134
134
  Report results to the user. If any check fails, fix and re-verify.
135
135
 
@@ -159,4 +159,4 @@ Report results to the user. If any check fails, fix and re-verify.
159
159
  - MUST keep subagents focused — one task per subagent, clear boundaries
160
160
  - MUST report blockers to the user immediately rather than working around them silently
161
161
  - MUST run `yarn generate` after creating or modifying module convention files
162
- - MUST run `yarn mercato db generate` after creating or modifying entities (and confirm migration with user before applying)
162
+ - MUST run `yarn db:generate` after creating or modifying entities (and confirm migration with user before applying)
@@ -57,9 +57,8 @@ src/modules/<module_id>/
57
57
  ├── setup.ts # Tenant init, role features
58
58
  ├── di.ts # Awilix DI registrations
59
59
  ├── events.ts # Typed event declarations (if needed)
60
- ├── entities/
61
- │ └── <Entity>.ts # MikroORM entity class
62
60
  ├── data/
61
+ │ ├── entities.ts # MikroORM entity classes
63
62
  │ └── validators.ts # Zod validation schemas
64
63
  ├── api/
65
64
  │ ├── get/
@@ -81,12 +80,12 @@ src/modules/<module_id>/
81
80
 
82
81
  ## 3. Create Entity
83
82
 
84
- **File**: `src/modules/<module_id>/entities/<Entity>.ts`
83
+ **File**: `src/modules/<module_id>/data/entities.ts`
85
84
 
86
85
  ### Template
87
86
 
88
87
  ```typescript
89
- import { Entity, Property, PrimaryKey, Index } from '@mikro-orm/core'
88
+ import { Entity, Index, PrimaryKey, Property } from '@mikro-orm/decorators/legacy'
90
89
  import { v4 } from 'uuid'
91
90
 
92
91
  @Entity({ tableName: '<entities>' }) // plural, snake_case
@@ -132,6 +131,7 @@ export class <Entity> {
132
131
  - PK: always `uuid` with `v4()` default
133
132
  - MUST include `organization_id` + `tenant_id` with `@Index()`
134
133
  - MUST include `created_at`, `updated_at`, `deleted_at`, `is_active`
134
+ - Entity decorators MUST come from `@mikro-orm/decorators/legacy`
135
135
  - Cross-module references: store FK as `uuid` field (e.g., `customer_id`) — never use ORM `@ManyToOne`
136
136
  - Use `@Property({ type: 'jsonb' })` for flexible/nested data
137
137
  - Use `@Property({ type: 'varchar', length: N })` for bounded strings
@@ -179,7 +179,7 @@ Use `makeCrudRoute` for standard CRUD. Each HTTP method lives in its own file.
179
179
 
180
180
  ```typescript
181
181
  import { makeCrudRoute } from '@open-mercato/shared/lib/crud/make-crud-route'
182
- import { <Entity> } from '../../entities/<Entity>'
182
+ import { <Entity> } from '../../data/entities'
183
183
 
184
184
  const handler = makeCrudRoute({
185
185
  entity: <Entity>,
@@ -202,7 +202,7 @@ export const openApi = {
202
202
 
203
203
  ```typescript
204
204
  import { makeCrudRoute } from '@open-mercato/shared/lib/crud/make-crud-route'
205
- import { <Entity> } from '../../entities/<Entity>'
205
+ import { <Entity> } from '../../data/entities'
206
206
  import { create<Entity>Schema } from '../../data/validators'
207
207
 
208
208
  const handler = makeCrudRoute({
@@ -226,7 +226,7 @@ export const openApi = {
226
226
 
227
227
  ```typescript
228
228
  import { makeCrudRoute } from '@open-mercato/shared/lib/crud/make-crud-route'
229
- import { <Entity> } from '../../entities/<Entity>'
229
+ import { <Entity> } from '../../data/entities'
230
230
  import { update<Entity>Schema } from '../../data/validators'
231
231
 
232
232
  const handler = makeCrudRoute({
@@ -250,7 +250,7 @@ export const openApi = {
250
250
 
251
251
  ```typescript
252
252
  import { makeCrudRoute } from '@open-mercato/shared/lib/crud/make-crud-route'
253
- import { <Entity> } from '../../entities/<Entity>'
253
+ import { <Entity> } from '../../data/entities'
254
254
 
255
255
  const handler = makeCrudRoute({
256
256
  entity: <Entity>,
@@ -589,7 +589,7 @@ Add to `src/modules.ts`:
589
589
 
590
590
  ```bash
591
591
  yarn generate # Discover module files, update .mercato/generated/
592
- yarn db:generate # Create migration for new entity
592
+ yarn db:generate # Probe/create migration for the new entity
593
593
  ```
594
594
 
595
595
  ### Step 3: Review Migration
@@ -599,11 +599,13 @@ Check the generated migration file in `src/modules/<module_id>/migrations/`. Ver
599
599
  - All columns present with correct types
600
600
  - Indexes on `organization_id`, `tenant_id`
601
601
  - No unexpected changes
602
+ - `migrations/.snapshot-open-mercato.json` was updated to the post-change schema
603
+ - Unrelated generated migrations were deleted from the diff
602
604
 
603
605
  ### Step 4: Apply & Test
604
606
 
605
607
  ```bash
606
- yarn db:migrate # Apply migration (confirm with user first)
608
+ yarn db:migrate # Apply migration only after explicit user confirmation
607
609
  yarn dev # Start dev server
608
610
  ```
609
611
 
@@ -631,7 +633,7 @@ yarn dev # Start dev server
631
633
  - [ ] ACL features declared and wired in `setup.ts`
632
634
  - [ ] Module registered in `src/modules.ts` with `from: '@app'`
633
635
  - [ ] `yarn generate` run after creating files
634
- - [ ] `yarn db:generate` run after creating entity
636
+ - [ ] Migration SQL is scoped to this entity and `.snapshot-open-mercato.json` is updated
635
637
  - [ ] No `any` types
636
638
  - [ ] No hardcoded user-facing strings
637
639
  - [ ] No direct ORM relationships to other modules
@@ -651,6 +653,8 @@ yarn dev # Start dev server
651
653
  - **MUST** declare ACL features and wire them in `setup.ts` `defaultRoleFeatures`
652
654
  - **MUST** register module in `src/modules.ts` with `from: '@app'`
653
655
  - **MUST** run `yarn generate` after creating module files
654
- - **MUST** run `yarn db:generate` after creating/modifying entities
656
+ - **MUST** create or keep a scoped migration after creating/modifying entities and update `.snapshot-open-mercato.json`
657
+ - **MUST NOT** commit unrelated migrations emitted by `yarn db:generate`
658
+ - **MUST NOT** run `yarn db:migrate` without explicit user confirmation
655
659
  - **MUST NOT** create ORM relationships (`@ManyToOne`, `@OneToMany`) to entities in other modules
656
660
  - **MUST NOT** edit `.mercato/generated/*` files manually
@@ -128,17 +128,21 @@ yarn typecheck
128
128
 
129
129
  1. **Did you create a migration after adding/changing the entity?**
130
130
  ```bash
131
- yarn db:generate # Creates migration file
131
+ yarn db:generate # Probes/creates migration file
132
132
  ```
133
- Fix: Run `yarn db:generate` to create the migration.
133
+ Fix: Run `yarn db:generate` to inspect the required migration, then keep only the scoped SQL for your module and update `src/modules/<module_id>/migrations/.snapshot-open-mercato.json`.
134
134
 
135
- 2. **Did you apply the migration?**
135
+ 2. **Is the entity declared in the right file with the right imports?**
136
+ Entity classes belong in `src/modules/<module_id>/data/entities.ts` and decorators must come from `@mikro-orm/decorators/legacy`.
137
+ Fix: move stale `entities/<Entity>.ts` patterns into `data/entities.ts` and fix the imports before regenerating the migration.
138
+
139
+ 3. **Did you apply the migration?**
136
140
  ```bash
137
141
  yarn db:migrate # Applies pending migrations
138
142
  ```
139
143
  Fix: Run `yarn db:migrate`.
140
144
 
141
- 3. **Is the migration file correct?**
145
+ 4. **Is the migration file correct?**
142
146
  Check `src/modules/<module_id>/migrations/` for the latest migration.
143
147
  Verify it has the expected columns and types.
144
148
  Fix: If wrong, delete the migration file, fix the entity, and regenerate.
@@ -158,16 +162,21 @@ yarn typecheck
158
162
  Never edit `node_modules/@open-mercato/*`.
159
163
  Fix: Revert changes to node_modules. Use UMES extensions instead, or eject the module.
160
164
 
165
+ 3. **Is a module snapshot stale?**
166
+ Check whether the generated SQL recreates a table or column that already has a committed migration.
167
+ Fix: update that module's `migrations/.snapshot-open-mercato.json` to include the already-migrated schema, then re-run `yarn db:generate` and expect `no changes`.
168
+
161
169
  ### Entity changes not reflected
162
170
 
163
171
  **Symptoms**: Changed entity file but API still returns old schema
164
172
 
165
173
  **Checklist**:
166
174
 
167
- 1. Run `yarn generate` entity discovery is cached
168
- 2. Run `yarn db:generate` — schema needs a migration
169
- 3. Run `yarn db:migrate` — migration needs to be applied
170
- 4. Restart `yarn dev` — server caches entity metadata
175
+ 1. Verify the entity lives in `src/modules/<module_id>/data/entities.ts` and imports decorators from `@mikro-orm/decorators/legacy`
176
+ 2. Run `yarn generate` — entity discovery is cached
177
+ 3. Run `yarn db:generate` — schema needs a migration
178
+ 4. Run `yarn db:migrate` — migration needs to be applied
179
+ 5. Restart `yarn dev` — server caches entity metadata
171
180
 
172
181
  ---
173
182
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/cli",
3
- "version": "0.5.1-develop.2972.6c5cd4a1c3",
3
+ "version": "0.5.1-develop.2975.ccbadc8198",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "exports": {
@@ -59,8 +59,8 @@
59
59
  "@mikro-orm/decorators": "^7.0.10",
60
60
  "@mikro-orm/migrations": "^7.0.10",
61
61
  "@mikro-orm/postgresql": "^7.0.10",
62
- "@open-mercato/queue": "0.5.1-develop.2972.6c5cd4a1c3",
63
- "@open-mercato/shared": "0.5.1-develop.2972.6c5cd4a1c3",
62
+ "@open-mercato/queue": "0.5.1-develop.2975.ccbadc8198",
63
+ "@open-mercato/shared": "0.5.1-develop.2975.ccbadc8198",
64
64
  "cross-spawn": "^7.0.6",
65
65
  "pg": "8.20.0",
66
66
  "semver": "^7.7.4",
@@ -70,10 +70,10 @@
70
70
  "typescript": "^5.9.3"
71
71
  },
72
72
  "peerDependencies": {
73
- "@open-mercato/shared": "0.5.1-develop.2972.6c5cd4a1c3"
73
+ "@open-mercato/shared": "0.5.1-develop.2975.ccbadc8198"
74
74
  },
75
75
  "devDependencies": {
76
- "@open-mercato/shared": "0.5.1-develop.2972.6c5cd4a1c3",
76
+ "@open-mercato/shared": "0.5.1-develop.2975.ccbadc8198",
77
77
  "@types/jest": "^30.0.0",
78
78
  "jest": "^30.3.0",
79
79
  "ts-jest": "^29.4.9"