Review code changes for Open Mercato compliance with architecture, security, conventions, and quality rules. Use this skill when reviewing pull requests, reviewing code changes, performing code review, auditing code quality, or when asked to review files, diffs, or commits. Covers module structure, naming conventions, data security, UI patterns, event/cache/queue rules, and anti-patterns.
Review code changes against Open Mercato's architecture rules, security requirements, naming conventions, and quality standards. Produce actionable, categorized findings.
.ai/specs/ for active specs on the module. Read .ai/lessons.md for known pitfalls.yarn template:sync. If drift is reported, ask the user whether to sync now; if approved, run yarn template:sync:fix and include synced files in the change.BACKWARD_COMPATIBILITY.mdAGENTS.mdreferences/review-checklist.md. Flag violations with severity, file, line, and fix suggestion.NEVER claim code is "ready to ship", "ready to merge", or "CI will pass" without running these checks first and confirming they all pass. This gate mirrors exactly what .github/workflows/ci.yml runs on every PR to develop/main. If any step fails, it MUST be fixed before the review can pass.
Run these commands and verify each one exits successfully (exit code 0):
| # | Command | What it checks | If it fails |
|---|---|---|---|
| 1 | yarn build:packages | All packages compile | Fix TypeScript/build errors in the changed package |
| 2 | yarn generate | Module auto-discovery files are up to date | Run it — it generates missing files |
| 3 | yarn build:packages | Rebuild with generated files included | Fix any type errors exposed by generated files |
| 4 | yarn i18n:check-sync | All 4 locale files (en, de, es, pl) + template locales are in sync | Add missing i18n keys to all locale files |
| 5 | yarn i18n:check-usage | No unused or missing i18n keys | Remove unused keys or add missing ones (CI uses continue-on-error so non-blocking, but still report) |
| 6 | yarn typecheck | TypeScript types are correct across all 14+ packages | Fix type errors — do NOT dismiss as "pre-existing" |
| 7 | yarn test | All unit tests pass across all packages | Fix failing tests — do NOT dismiss as "flaky" or "pre-existing" |
| 8 | yarn build:app | The Next.js app builds successfully | Fix build errors |
yarn typecheck or yarn test fails, it is a Critical finding in the review — even if the failure appears unrelated to the current changes. The PR will fail CI regardless of whose fault it is.Use this structure for every review:
# Code Review: {PR title or change description}
## Summary
{1-3 sentences: what the change does, overall assessment}
## CI/CD Verification
| Gate | Status | Notes |
|------|--------|-------|
| `yarn build:packages` | PASS/FAIL | |
| `yarn generate` | PASS/FAIL | |
| `yarn build:packages` (rebuild) | PASS/FAIL | |
| `yarn i18n:check-sync` | PASS/FAIL | |
| `yarn i18n:check-usage` | PASS/FAIL/WARN | (non-blocking in CI) |
| `yarn typecheck` | PASS/FAIL | |
| `yarn test` | PASS/FAIL | |
| `yarn build:app` | PASS/FAIL | |
## Findings
### Critical
{Violations that MUST be fixed before merge — security, data integrity, tenant isolation}
### High
{Architecture violations, missing required exports, broken conventions}
### Medium
{Style issues, missing best practices, suboptimal patterns}
### Low
{Suggestions, minor improvements, nits}
## Backward Compatibility
- [ ] No contract surface removed or renamed without deprecation bridge
- [ ] No event IDs renamed or removed
- [ ] No widget injection spot IDs renamed or removed
- [ ] No API route URLs renamed or removed
- [ ] No existing response schema fields removed
- [ ] No database columns/tables renamed or removed
- [ ] No DI service names renamed or removed
- [ ] No ACL feature IDs renamed or removed
- [ ] No public import paths removed without re-export bridge
- [ ] No required type fields removed or narrowed
- [ ] No function signatures changed in a breaking way
- [ ] Deprecation protocol followed (if applicable): `@deprecated` JSDoc, bridge re-export, spec with migration section
## Checklist
- [ ] No `any` types introduced
- [ ] All API routes export `openApi`
- [ ] Validators in `data/validators.ts` (not inline)
- [ ] Tenant isolation: queries filter by `organization_id`
- [ ] No hardcoded user-facing strings
- [ ] CRUD routes use `makeCrudRoute` with `indexer`
- [ ] Events declared in `events.ts` before emitting
- [ ] Workers/subscribers export `metadata`
- [ ] Custom fields use `collectCustomFieldValues()`
- [ ] `modules:prepare` needed after file additions
- [ ] No cross-module ORM relationships
- [ ] Encryption helpers used instead of raw `em.find`
- [ ] Forms use `CrudForm`, tables use `DataTable`
- [ ] Non-`CrudForm` backend writes use `useGuardedMutation(...).runMutation(...)` with `retryLastMutation` in context
- [ ] `apiCall` used instead of raw `fetch`
- [ ] ACL features mirrored in `setup.ts` `defaultRoleFeatures`
- [ ] ACL features use object format `{ id, title, module }` (not string arrays)
- [ ] `yarn template:sync` passes for `apps/mercato/src/{app,modules}` vs `packages/create-app/template/src/{app,modules}`
- [ ] Behavior changes covered by unit and/or integration tests (or explicitly justified as not applicable)
- [ ] No empty `catch` blocks (all catches must handle, log, rethrow, or explicitly document intentional ignore)
- [ ] New migrations are scoped to intended entities only (no unrelated bulk drop/alter/create statements)
- [ ] New or renamed spec files use `{YYYY-MM-DD}-{slug}.md` in `.ai/specs` or `.ai/specs/enterprise`
- [ ] No two spec files collapse to the same normalized `{YYYY-MM-DD}-{slug}.md` target when legacy `SPEC-*` / `SPEC-ENT-*` prefixes are removed
Omit empty severity sections. Mark passing checklist items with [x] and failing with [ ] plus explanation.
| Severity | Criteria | Action |
|---|---|---|
| Critical | Security vulnerability, cross-tenant data leak, data corruption risk, missing auth guard, backward compatibility violation (breaking contract surface without deprecation bridge) | MUST fix before merge |
| High | Architecture violation, missing required export (openApi, metadata), broken module contract, missing deprecation annotation on contract change | MUST fix before merge |
| Medium | Convention violation, suboptimal pattern, missing best practice | Should fix |
| Low | Style suggestion, minor improvement, readability | Nice to have |
These are the highest-impact rules. For the full checklist, see references/review-checklist.md.
BACKWARD_COMPATIBILITY.md for the full list@deprecated JSDoc → bridge re-export/alias → removal after one minor versionorganization_id for tenant-scoped entities — never expose cross-tenant datanew directlydata/extensions.ts — never add columns to another module's tabledata/validators.ts — never trust raw inputfindWithDecryption instead of raw em.find/em.findOnerequireAuth, requireRoles, requireFeatures)fieldPolicy.excluded in search configyarn db:generatewithAtomicFlush when mutating entities across phases that include queries__originalEntityData resetFor every migration in the diff, reviewer MUST:
drop constraint, drop table, or broad alter table across many modules).Examples of suspicious patterns that MUST be flagged:
drop, bulk constraint removals) without matching entity changes.id)id, created_at, updated_at, deleted_at, is_active, organization_id, tenant_idapps/mercato/src/ — use apps/mercato/src/modules/@open-mercato/shared) has zero domain dependencies — MUST NOT import from @open-mercato/core| File | Required Export | Rule |
|---|---|---|
| API routes | openApi | MUST export for doc generation |
| API routes | metadata | MUST declare auth guards |
| Subscribers | metadata with { event, persistent?, id? } | MUST for auto-discovery |
| Workers | metadata with { queue, id?, concurrency? } | MUST for auto-discovery |
events.ts | eventsConfig via createModuleEvents() with as const | MUST for type-safe events |
acl.ts | features | MUST use object entries { id, title, module } and mirror IDs in setup.ts defaultRoleFeatures |
search.ts | searchConfig with checksumSource in every buildSource | MUST for change detection |
CrudForm — never custom form implementationsCrudForm, every write (POST/PUT/PATCH/DELETE) MUST go through useGuardedMutation(...).runMutation(...)useGuardedMutation context MUST include retryLastMutation so conflict resolution can auto-retry without extra save promptsDataTable — never manual table markupflash() — never alert() or custom toastapiCall/apiCallOrThrow — never raw fetchreadJsonSafe(response, fallback) — never .json().catch()createCrudFormError(message, fieldErrors?) — never raw throwCmd/Ctrl+Enter (submit), Escape (cancel)RowActions items MUST have stable id values (edit, open, delete)pageSize MUST be <= 100useT() client-side, resolveTranslations() server-side — never hardcode stringsany types — use zod + z.infer, narrow with runtime checkscatch blocks — catch blocks MUST handle, log, rethrow, or include explicit rationale for intentional ignoreparseBooleanToken/parseBooleanWithDefault from @open-mercato/shared/lib/booleanWhen reviewing, pay special attention to:
BACKWARD_COMPATIBILITY.md. If a type field is removed, a function signature changed, an event ID renamed, a spot ID removed, a DB column dropped, an import path moved, or a DI name changed — flag as Critical unless the deprecation protocol is followed (bridge + @deprecated + spec).modules:prepare is needed. Verify auto-discovery paths are correct.yarn db:generate is needed. Look for missing tenant scoping columns.
2a. Migration presence for entity changes: If any entity schema file changed (for example data/entities.ts), verify the diff also contains a corresponding generated migration file. If missing, raise at least a High finding instructing to run yarn db:generate.
2b. Migration files: Inspect SQL content, not only filename. Autogenerated does not mean valid; reject oversized/unrelated churn.openApi export, auth guards, zod validation, tenant filtering.events.ts with as const. Check subscriber exists.checksumSource, fieldPolicy.excluded for sensitive fields, formatResult for token strategy.metadata export, concurrency <= 20.withAtomicFlush for multi-phase mutations.defaultRoleFeatures matches acl.ts features. Hooks MUST be idempotent.
9a. ACL shape validation: Verify acl.ts exports feature objects (with id, title, module) rather than raw string IDs; flag wrong shape as High because permission UI and role assignment can break.CrudForm/DataTable usage, flash() for feedback, keyboard shortcuts, loading/error states.CrudForm), verify useGuardedMutation is wired for all writes and context includes retryLastMutation; for custom write API routes, verify validateCrudMutationGuard + runCrudMutationGuardAfterSuccess are both wired.{YYYY-MM-DD}-{slug}.md, legacy numbered files are not copied forward into new work, and no two files would resolve to the same normalized date+slug target.yarn template:sync finds drift in src/app or src/modules (especially layout/routes), ask the user whether to sync; when approved, run yarn template:sync:fix and include those updates.Check these known pitfalls from .ai/lessons.md:
buildLog(): Always load via forked EntityManager or refresh: trueEntityManagerextractUndoPayload() from @open-mercato/shared/lib/commands/undo.ts