---
name: ba-audit-menu
description: >
  Audits the menu (Application / Module) of a `.smartstack/ba/` project — module
  semantics, cross-app duplicates, SmartStack Core collisions, orphans/empty,
  out-of-scope coherence, hierarchy code distinctness (MENU-001..008). Reads the
  tree, writes a verdict to `_audit/menu.md`. Run after `/ba-create-menu` or as
  part of pre-dev readiness.
allowed-tools: [Read, Write, Glob, Grep, Bash]  # Bash: the audit-ba engine (deterministic mechanical rules)
---

# ba-audit-menu — Menu (Application / Module) audit

You audit the menu structure of a `.smartstack/ba/` project against the rules
below and write a verdict file. The rules are unchanged from the SmartStack
convention; only the I/O is file-based.

## Deterministic engine — how this audit runs

The MECHANICAL rules of this dimension are evaluated by the shared `audit-ba`
CLI (see `/ba-audit-run`) — **never by reading the corpus yourself, never by
spawning per-module subagents** (the 394M-token incident shape). Your only
job here is the judgment residue.

1. **Run the engine, scoped to this dimension**:

   ```bash
   npx --prefer-offline tsx skills/business-analyse/audit-run/cli/audit-ba/index.ts \
     --spec '{"baRoot":".smartstack/ba","scope":{"app":"<APP>"},"dimensions":["menu"]}'
   ```

2. **Exit 3 = parsing suspect -> STOP.** A control counter disagrees with the
   parser (`report.parseControl.perDoc`): fix the doc's form or report the
   parser bug, then re-run. Never « complete by hand » — no green verdict may
   be born from a silent parser.
3. **Arbitrate** every `report.judgmentNeeded[]` entry of this dimension (MENU-001, MENU-002, MENU-005) from its `question` + `excerpts` ONLY (they are complete by contract — needing more is a CLI bug to report, never a license to read the corpus). Write the decisions JSON to the scratchpad and re-run with `\"judgments\":\"<path>\"` — the CLI merges, consumes the pending items and rewrites the verdict itself (you never write verdict markdown).
4. **Chat summary** (3-6 lines, business terms): the PARSE TOTALS (say the
   counts — that is how a « 0 erreur » stays verifiable), err/warn counts,
   remaining judgments, and the fix skill each finding names.

The CLI writes the verdict to `.smartstack/ba/<APP>/_audit/menu.md`
(existing format — anchor, `Verdict :` header, emoji sections; `0 err` =
pass for the downstream gate). The rule texts below remain the AUTHORITATIVE
spec — the CLI registry is drift-tested against them.

## Scope

- **App scope** (default): audit one application — apply MENU-001..008 to every
  module under it. Don't scan sibling apps.
- **Module scope**: apply MENU-001, MENU-003, MENU-004, MENU-005, MENU-006, MENU-007, MENU-008 to
  that single module (skip MENU-002 — cross-app duplicates aren't a
  single-module concern).
- The verdict file lives at the audited app's `_audit/menu.md` either way.

## Rules

### MENU-001 — Module semantically fits its parent application
- `warn` if a module's business scope doesn't match the parent app; else `ok`.
- Decide from the menu tree only (app + module + section labels/codes). Don't use
  a hardcoded mapping — use business semantics. `PROSPECTS` under `CRM` → ok;
  `RECRUTEMENT` under `CRM` → warn (suggest the right app + reason).
- Fix: `/ba-create-menu`.

### MENU-002 — No duplicate module concept across applications
- `warn` if the same concept appears in 2+ apps (exact code OR semantic match,
  e.g. `CONTACTS` in CRM vs `CUSTOMERS` in SALES); else `ok`. Only at project /
  ≥2-sibling-app scope.
- Distinguish legitimate per-app reuse (a `SETTINGS` module per app is expected)
  from true duplication. Fix: `/ba-create-menu`.

### MENU-003 — No app/module duplicates a built-in platform app
- `err` on collision. The platform ships built-in applications INSIDE the package
  (invisible to a client `src/` scan), so a client menu must EXTEND them, never
  recreate them. Check every **application** AND **module** in the audited tree
  against this catalogue — whole-token / whole-phrase, accent- & case-insensitive;
  **never substring** (`HRManager` is not `hr`):

<!-- platform-apps:v1 — drift-tested against lib/platform-catalog.ts (edit BOTH or the suite fails) -->
| App (code) | Label | Personal | Extendable | Domain aliases (FR/EN) |
|---|---|---|---|---|
| administration | Administration | no | no | Admin, System, Système, Paramétrage système |
| support | Support | no | yes | Helpdesk, Ticketing, Billetterie, Assistance, SAV |
| hr | Human Resources | no | yes | RH, HR, Ressources Humaines, Ressources humaines, Personnel, GRH, Human Resources |
| api | API | no | yes | External API, Data export, Export de données, Intégrations externes |
| myspace | My Space | yes | no | Espace personnel, Mon espace, Personal workspace |
<!-- /platform-apps:v1 -->

- `err` cases:
  - a client **application** whose code/label/domain matches a built-in app
    (e.g. an `RH`/`HR` app → duplicates the platform `hr` app). **Exception**: an
    app node whose `## Contexte` explicitly declares it extends that built-in
    (contains "Extension de l'application plateforme `<code>`" / "extends the
    built-in platform app") AND adds only NEW modules is the intended pattern →
    `ok`, not a finding.
  - a client **module** that recreates a DISTINCTIVE built-in module — for `hr`:
    `employees`, `absences`, `my-absences`, `time`, `my-time`; for `support`:
    `tickets`. Generic modules (dashboard, reporting, configuration, organization,
    profile, …) are NOT a collision.
  - the cross-cutting technical features that must NEVER be user modules:
    Auth/SSO, Notifications, Workflows, Data export, Audit log.
  - any **application** named `SETTINGS`/`PARAMS`/`CONFIGURATION` (config is a
    module, not an app).
- For each `err`, name the offending code, the built-in it collides with, and the
  fix: drop the app and author the modules UNDER the built-in app
  (`/ba-create-menu` § "extend, never duplicate"). Fix: `/ba-create-menu`.

### MENU-004 — No orphan module, no empty app, no empty module
- `err`. Scoped to the audited app:
  - **orphan**: a module whose parent folder/app is missing or non-existent.
  - **empty app**: the app has zero modules.
  - **empty module**: a module has zero sections (can't expose any UI → blocks
    downstream phases).
- An empty *sibling* app is not a finding here (it's that sibling's own audit).
  Fix: `/ba-create-menu`.

### MENU-005 — No module contradicts the declared out-of-scope
- `warn` when a module's purpose contradicts an `## Hors-périmètre` declaration;
  `ok` when no contradiction (or no out-of-scope declared anywhere relevant —
  the explicit empty marker `_Aucune exclusion connue à ce stade._` counts as
  NO declaration here: there is nothing to confront against an assertion of
  absence).
- Compare each module (label + `## Contexte`) against the **project root**'s
  `## Hors-périmètre`, the **parent app**'s, and the module's own. The
  contradiction is **semantic**, not lexical (a `BILLING` module under an app
  whose out-of-scope says "la facturation est couverte ailleurs" is a hit; a mere
  shared keyword on a different function is not).
- `warn` (not `err`) — the user may have evolved the scope. Suggest: trim the
  out-of-scope, relocate, or drop the module. Fix: `/ba-create-menu`.

### MENU-006 — Hierarchy levels are distinct (no code collision)
- `warn` when one or more app/module/section triples share the same code;
  `ok` when every level carries its own distinct code.
- **Check** (deterministic, from the folder tree): for every module under the
  audit scope, assert `module code !== application code`. For every section,
  assert `section code !== module code` AND `section code !== application
  code`. Case-insensitive compare (`BUDGETS` collides with `Budgets`). Codes
  are the kebab folder names / `index.md` anchor codes.
- **Why this is advisory** (`warn`, not `err`): a mono-domain project may
  legitimately produce a trivial draft (the user types "Budgets" everywhere
  during a 30-second sketch) before `/ba-create-menu` refines the tree.
  Surface the issue so the user splits the levels; don't block on it.
- **Why it matters downstream**: a flat `app=module=section` hierarchy
  produces screen codes like `SCR-BUDGETS-BUDGETS-BUDGETS-001` — technically
  valid, but the screens phase can't build a useful navigation (no
  module-home, no section-home — they'd all point at the same node) and the
  generated React frontend routes collide (observed runtime regression,
  2026-05-11).
- `warn`: list each collision (`APP=MOD` / `MOD=SEC` pairs) and suggest the
  rename or level split. Fix: `/ba-create-menu`.

### MENU-007 — Every required node carries a substantial Contexte
- `err` when an app- or module-level `index.md` has no `## Contexte` (or an
  empty one); `warn` when a required Contexte is under ~120 characters (a
  one-liner carries neither the WHO, the WHAT nor the LIMITS — 3-5 sentences
  expected) or when a section-level Contexte is empty; `ok` otherwise.
- **Check** (deterministic): parse each node `index.md` under the audit scope
  (app, its modules, their sections). A node with NO `index.md` at all is
  MENU-004's finding, never a double-count here.
- **Why it matters downstream**: the Contexte is what `/ba-create-prd`, every
  downstream phase and the client-sources routing (`scopes` of
  `.smartstack/sources/index.json`) anchor on. An empty Contexte gives the
  whole chain nothing to reason from — and nothing to CITE (`- **Sources** :
  SRC-NNN §n` closes the Contexte when a client source grounds it).
- Fix: `/ba-create-menu` (re-author the node), grounded in the registered
  sources when they exist.

### MENU-008 — Hors-périmètre is declared (exclusions or the explicit empty marker)
- `err` when an app- or module-level `index.md` has no `## Hors-périmètre`
  heading, or has the heading with neither a bullet nor the explicit empty
  marker `_Aucune exclusion connue à ce stade._`; `warn` when a section-level
  node lacks the heading (encouraged, not blocking); `ok` otherwise.
- **Doctrine**: the absence of exclusions is an ASSERTION, not an omission —
  it is WRITTEN (the empty marker), so a missing section can never be read as
  « the analyst considered it and found nothing ».
- **Why it matters downstream**: `/ba-create-prd` cascades every non-empty
  out-of-scope into `## Non-goals` (the explicit empty marker is NOT copied),
  and MENU-005 confronts modules against these declarations — an undeclared
  Hors-périmètre silently disables both.
- Fix: `/ba-create-menu`.

## Output

Write `_audit/menu.md` per the doc-templates skeleton:
- Header `# Audit menu — <APP>` + `_<date> · Verdict : <emoji> N warn · M err · K ok_`.
- `## ✅ Conforme`, `## ⚠️ Avertissements`, `## ❌ Bloquants` sections; one bullet
  per finding. For `warn`/`err`: what's wrong (offending codes **bold**), why it
  matters, and a `→` fix naming `/ba-create-menu`.
- Re-Write the whole file each run (overwrite — it's a fresh verdict).

Then a 3–6 line chat summary in the user's language — business terms, not rule
codes. If any `err`, state clearly that the menu must be fixed before moving on.

## Used by the readiness orchestrator

`/ba-audit-pre-dev` runs every dimension and aggregates the verdicts. When invoked
by it, still write `_audit/menu.md` as usual — the orchestrator reads these files.
