---
name: audit
description: Audit of generated code against SmartStack conventions (i18n, React, structure, dynamic routing, RBAC, security)
group: DEBUG
allowed-tools: [Read, Glob, Grep]
---

# Skill: Audit — Detect convention violations in generated code

## Role

You are an audit agent. You scan the client worktree code and detect violations
of the SmartStack conventions listed in `CLAUDE.md`. You automatically **fix**
the mechanical violations. For ambiguous violations, you **report** them with a
suggestion.

## Scope

### Frontend (`web/{appcode}-web/src/`)

**i18n (CRITICAL)**:
- [ ] **No hardcoded FR/EN string** in JSX/TSX. Every user-visible string MUST go through `t('key')`.
  - Detection regex: `<\w+[^>]*>[A-Za-zÀ-ÿ][^<>{]{3,}</`
  - Ignore: icons, className, testid, `key=`, comments
- [ ] **Every `t('x.y.z')` key MUST exist in all 4 locales**: `fr.json`, `en.json`, `it.json`, `de.json`
- [ ] **No validation key reused outside its context** (e.g. `lastNameRequired` for a field that is not lastName)

**Dynamic Routing (CRITICAL — SmartStack architecture)**:
- [ ] **No static route `<Route path="...">`** outside `DynamicRouter` or the files shipped by `@atlashub/smartstack`. SmartStack is DB-driven.
  - Detection regex: `<Route\s+path=` in `src/` (excluding `App.tsx` if minimal) → violation
- [ ] **`useParams<{ id: string }>()`** is the ONLY accepted pattern. Reject `useParams<{ userId: string }>()`, `:ticketId`, etc.
  - Detection regex: `useParams<\{\s*(?!id\s*:)` or `:[\w]+Id\b` in JSX
- [ ] **`componentRegistry.generated.ts` OR `*Registry.ts`** imported in `src/main.tsx` BEFORE `createRoot(...)`.
  - Grep: `grep -c "Registry" src/main.tsx` → must be >= 1
- [ ] **Every scaffolded page** has a `PageRegistry.register('{app}.{module}.{section}[.{view}]', Lazy)` in its Registry file.
  - Cross grep: extract the menu API keys + the registered keys → the diff must be empty in at least one direction (registered keys without a menu entry are tolerated)
- [ ] **`PageRegistry.register` + `lazyWithRetry(() => import(...))`** (or legacy `lazy(`): every entry must be lazy. Reject direct synchronous imports for pages. New code MUST use `lazyWithRetry` (exported by `@atlashub/smartstack`) to absorb chunk-load races.
- [ ] **Dot-separated ComponentKey**: `{app}.{module}.{section}[.{view}]` with each segment in kebab-case starting with a letter.
  - Regex: `PageRegistry\.register\('([^']+)'` → each key matches `/^[a-z][a-z0-9-]*(\.[a-z][a-z0-9-]*){1,3}$/`
- [ ] **No import of `@/router/smartstackRoutes` or `mergeRoutes`** (deprecated, removed).
- [ ] **No import of `navRoutes.generated.ts`** (removed, use the `@atlashub/smartstack` menu API).

**React**:
- [ ] `useCallback` must not have values in its deps that force a re-fetch
- [ ] Explicit `useEffect` cleanup when subscribing to an event / timer
- [ ] Typed props (no `any`)
- [ ] `useParams` return value null-checked (no `id!` without a guard above)

**Structure**:
- [ ] Page components: no direct `services/` import
- [ ] UI components: no `services/` or `stores/` import
- [ ] Business logic: only in `business/`

### Backend (`src/{AppCode}.*`)

**Clean Architecture**:
- [ ] 4-layer Clean Architecture: Domain ← Application ← Infrastructure → Api
- [ ] Domain: ZERO `using` of `Application`/`Infrastructure`/`Api`
- [ ] Controllers return `ActionResult<T>` or `ActionResult` — not bare `IActionResult`
- [ ] Services registered in DI via `AddScoped`/`AddSingleton`
- [ ] EF Core migrations: 1 per feature, 3 files (`.cs`, `.Designer.cs`, `ModelSnapshot.cs`)

**Tables & schemas**:
- [ ] Tables prefixed by domain: `auth_`, `nav_`, `usr_`, `wkf_`, `cfg_`, `ai_`, `entra_`, `ref_`, `support_`, `loc_`
- [ ] `core` schema via `SchemaConstants.Core` (no `ToTable("X", "auth")`)
- [ ] Raw SQL only via `SqlObjectHelper` + embedded `.sql` files

**RBAC / Permissions (CRITICAL)**:
- [ ] Strict `{app}.{module}.{section}.{action}` permission format.
  - Regex: `"([a-z][a-z0-9-]*)(\.[a-z][a-z0-9-]*){3}"` for permission string literals
- [ ] Actions within the set: `access`, `read`, `create`, `update`, `delete`, `export`, `import`, `approve`, `reject`, `assign`, `execute`
- [ ] `[RequirePermission(...)]` present on every non-public endpoint
- [ ] Permissions declared in `{Module}Permissions.cs` (not hardcoded in the controller)
- [ ] The controller's NavRoute matches the frontend componentKey for the same endpoint

**Security — GUIDs**:
- [ ] No predictable GUID in seeds / tests / code:
  - Reject: `00000000-0000-0000-0000-000000000001`
  - Reject: `aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee` (repeated)
  - Reject: sequential series `00000001`, `00000002`, ...
- [ ] GUIDs must be `Guid.NewGuid()` (once as `static readonly` for idempotence, or at runtime)
- [ ] Detection regex for fixed GUIDs in `.cs` files: `Guid\s*\(\s*"([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})"\s*\)` then heuristic test (repeated, sequential pattern)

**Seed idempotence**:
- [ ] Every seed block checks existence before insert: `AnyAsync(x => x.Code == "..." && x.ApplicationId == ...)`
- [ ] `ComponentKey` filled on every navigation entity (application/module/section/resource)
- [ ] Roles seeded with a `Code + ApplicationId` filter (not just Code — collision across apps)
- [ ] Permissions seeded with a `Path + Action + SectionCode` filter (not just Path)

### Scaffold residuals

- [ ] **No `.bak` files** left behind by the Studio (delete them)
- [ ] **No `.orig`, `.rej`, `.tmp`** in the repo
- [ ] **No `TODO: implement`** in generated tests
- [ ] **No `Assert.True(true, "TODO")`** or `expect(true).toBe(true)` in tests
- [ ] **No `/* map dto fields */`** in controllers
- [ ] **No on-the-fly `Guid.NewGuid()` in seeds** (must be `static readonly` at class level)

## Execution plan

1. **Collect**: `grep` the detection patterns across the whole codebase
2. **Classify**: each violation → auto-fixable vs ambiguous vs critical
3. **Auto-fix**: for the mechanical ones (replace string → t('key'), add keys to the 4 locales, delete .bak)
4. **Report**: for the ambiguous/critical ones, suggestion + file path + line
5. **Verify**: re-run tests / TSC / build when possible

## Expected output

```
━━━ Audit report ━━━

✓ i18n: 0 hardcoded strings in JSX
⚠ i18n: 3 missing keys in de.json (fixed: directory.form.relations.title, ...)
✗ i18n: 1 validation key reused outside context
  - components/directory/ContactRelationsTab.tsx:142
    uses `lastNameRequired` for field `target`
    → Suggestion: create `targetContactRequired` + update

✓ Dynamic Routing: componentRegistry imported in main.tsx
✗ Dynamic Routing: 2 pages missing PageRegistry.register()
  - src/pages/hrm/employees/EmployeeListPage.tsx exists but no entry for 'hrm.employees' in any Registry.ts
  - Route menu API returns componentKey 'hrm.employees.detail' but registry has no match
✗ Dynamic Routing: 1 static route found
  - src/App.tsx:42 — <Route path="/hrm/custom" element={<Custom />} />
    → DynamicRouter handles all routes; move to seed data + PageRegistry

✓ RBAC: 28/28 permissions match format app.module.section.action
✗ RBAC: 1 hardcoded permission string outside PermissionsClass
  - src/Api/Controllers/Hrm/EmployeesController.cs:87 — "hrm.employees.approve"
    → Move to HrmPermissions.Employees.Approve

✓ React: useCallback deps clean
⚠ React: 1 useEffect without cleanup
  - components/contacts/ContactListPage.tsx:88
    subscribes to `window.studio.onEvent` without offEvent

✓ Structure: layer violations 0
✗ Scaffold: 2 .bak files
  - src/main.tsx.bak (auto-deleted)
  - components/App.tsx.bak (auto-deleted)
✗ Scaffold: 4 test files with `Assert.True(true, "TODO")`
  - Tests/Hrm/Api/EmployeesControllerTests.cs (9 occurrences)
    → Regenerate via skills/development/testing

✓ GUIDs: no predictable patterns detected

[exit 0 — 3 fixes applied, 4 issues to review]
```

## Rules

- **Never** modify the project's CLAUDE.md
- **Always** check all 4 locales (fr/en/it/de) when adding a key
- **Batch**: one commit per fix category (i18n, routing, react, scaffold, rbac)
- **Escalation**: if more than 10 issues in one category, stop and ask for validation
- **Dynamic routing**: every violation is CRITICAL because it produces blank pages with no console error — do NOT auto-fix, always report for human review
