---
name: ba-audit-run
description: >
  ONE deterministic pass over the whole `.smartstack/ba/` corpus — the
  audit-ba CLI loads every doc once, runs the ~134 MECHANICAL audit rules of
  all 11 dimensions (menu, sections, actors, use-cases, rules, rbac,
  data-model, screens, cross-dimension, cross-ref-code, sources), publishes the parse
  totals, writes every `_audit/<dim>.md` verdict and `_audit/audit-ba.json`,
  and FAILS CLOSED on parser/control divergence (exit 3 « parsing suspect »).
  The LLM's only job here is arbitrating the compact `judgmentNeeded[]`
  excerpts (cross-application judgments — the only ones that block; the
  internal ones are an advisory `judgmentAdvisory[]`) — NEVER re-reading the
  corpus. Use for « audite le BA », « audit complet », « relance l'audit
  après mise à jour des skills ». Replaces the per-module LLM audit campaign
  (394M tokens on ImmoHub) for every mechanical rule.
argument-hint: "[APP[/MODULE]] [--strict] [--project-root <dir>]"
allowed-tools: [Read, Write, Glob, Bash]
---

# ba-audit-run — deterministic whole-project BA audit

## The iron rule — NEVER audit by reading the corpus

**You MUST NOT apply audit rules by reading `use-case.md` / `entité.md` /
`règles-métier.md` / `rbac.md` / `screen.md` yourself, and you MUST NOT spawn
subagents to do it per module.** That is the 394-million-token incident shape:
21 agents × 240k context per call for rules a CLI evaluates in seconds. The
CLI is the ONLY sanctioned evaluator of the mechanical rules; your judgment is
needed ONLY on the `judgmentNeeded[]` excerpts it hands you.

## Workflow

1. **Run the CLI** (whole project by default; scope with `{"scope":{"app":…,"module":…}}`):

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

   Pass `projectRoot` whenever the generated app lives in the working
   directory — without it the cross-ref-code scan legs surface a warn
   (« code non scanné »), never a false green.

2. **Read the envelope** (stdout JSON). Exit codes:
   - `0` conforme · `1` warn-only · `2` ≥1 err — the audit RAN, findings are
     in `report.findings[]` and the verdicts are written;
   - `3` **parsing suspect** — STOP. A control counter disagrees with the
     parser (`report.parseControl.perDoc`). NEVER « complete by hand », never
     read the corpus to compensate: fix the doc's form or report the parser
     bug, then re-run. No green verdict may be born from a silent parser.
   - `4` usage/spec error.

   Read `warnings[]` too. **`audit.rule-contradiction`** means the run
   disagrees with ITSELF: a finding whose `dedupOf` names a primary rule
   (same evaluator, two ids) is `err` while that primary is `ok` on the same
   scope. That is a defect of this CLI, never of the corpus — do NOT edit
   the document (nor invent an entity) to silence the mirror: record the
   blocker as `audit.rule-contradiction` and hand the envelope to
   `/support-report`, which classifies it `rule-contradiction` from the same
   detector (lib/rule-contradictions) and bundles the corpus for reproduction.

3. **Arbitrate the judgments** — for each `report.judgmentNeeded[]` entry
   (cross-application, the only gating ones — see « Which judgments block »
   below), and optionally each `report.judgmentAdvisory[]` entry (internal,
   never blocking), decide from its `question` + `excerpts` ONLY (they are
   complete by contract; if they are not enough, that is a CLI bug to report,
   not a license to read the corpus). Write your decisions to a scratchpad file:

   ```json
   { "decisions": [ { "id": "J-3f9a0c1d2e4b", "ruleId": "DM-034", "dimension": "data-model",
       "scope": { "app": "CRM", "module": "PIPELINE" },
       "resolution": "ok|warn|err", "message": "…", "evidence": ["…"] } ] }
   ```

   ALWAYS copy the item's `id`: DM-034, BR-012, UC-023 and SEC-004 emit one
   item per pair, so several items share (rule, scope) — the `id` settles
   exactly one of them. The `id` fingerprints what was shown (question +
   excerpts): when the content changes, a stored decision goes stale (warning)
   and the question comes back. Without `id`, the legacy match on
   (rule, dimension, scope) takes the first pending item in report order.

4. **Re-run with the decisions** — the CLI merges them, consumes the pending
   items and rewrites the final verdicts itself (the LLM never writes verdict
   markdown):

   ```bash
   npx --prefer-offline tsx skills/business-analyse/audit-run/cli/audit-ba/index.ts \
     --spec '{"baRoot":".smartstack/ba","projectRoot":".","judgments":"<path>"}'
   ```

5. **Chat summary** (3–6 lines, business terms): parse totals (say the counts
   — that is how a « 0 erreur » is verifiable), err/warn counts, conventions
   detected (CONV-001/002), remaining cross-application judgments, and the
   next step (`/ba-create-*` skills named by the findings, or
   `/ba-audit-pre-dev`). Never present an advisory point as something the
   user must validate.

## Which judgments block — cross-application only

A validation is asked on what puts ANOTHER application's element in the
balance, never on what stays inside one application (doctrine 2026-10-06 —
single source: `CROSS_APP_JUDGMENT_RULES` in `cli/audit-ba/judgments.ts`).

| Pool | Rules | Effect while pending |
|------|-------|----------------------|
| `judgmentNeeded[]` — **cross-application** | MENU-002, SEC-004, XAPP-003, BR-012, UC-023, DM-034 | counted `· J à arbitrer` in the verdict header, `## 🧠 Jugement requis` + `ba:judgment` anchor → the dimension stays « non audité » for `/ba-audit-pre-dev` |
| `judgmentAdvisory[]` — **internal** | MENU-001, MENU-005, XAPP-002, SEC-003, SEC-008, UC-007, UC-011, UC-015, XD-002, XD-008, SCR-023 | listed under `## 💡 Revue conseillée (non bloquante)` + `ba:review` anchor, never counted, never gating; an arbitration is capped at `warn` |

A one-application project never has a judgment to settle: five of these
rules only emit on a concrete pair spanning two applications, XAPP-003 asks
one project-level question as soon as the project holds ≥ 2 applications. A new judgment rule is advisory until
it is listed in `CROSS_APP_JUDGMENT_RULES`, and it may only be listed when its
candidates span two applications.

## What the CLI writes (atomic, only under `_audit/`)

- `.smartstack/ba/_audit/audit-ba.json` — the machine record: totals,
  findings, judgmentNeeded (cross-application, gating), judgmentAdvisory
  (internal, never gating), parseControl, conventions, `rulesetVersion` +
  `sourcesHash` (STALENESS: a verdict whose anchor carries an older ruleset
  or a different sources hash predates the current corpus/rules — re-run,
  it costs a minute).
- Per-dimension `_audit/<dim>.md` verdicts at the existing locations
  (actors at the project root, menu per app, the rest per module — screens
  now MODULE-level; delete legacy per-section `screen.md` verdicts, the
  envelope warns about them).
- `.smartstack/ba/_audit/audit-ba.md` — project summary (conventions,
  project-scope findings).

## Conventions (requirement: the tooling adapts to the corpus)

A ≥90 % measured corpus convention (lowercase UC section segments, flat-kebab
error codes) is ONE project-scoped warn (CONV-001/002), never an error per
occurrence — BR-011 defers to CONV-002. `--strict` (spec `"strict":true`)
restores the per-occurrence letter of the rules.

## Relationship with the per-dimension skills

The `/ba-audit-*` skills remain the JUDGMENT layer: invoked on one dimension,
they run THIS CLI scoped (`"dimensions":["use-cases"]`), arbitrate their
dimension's `judgmentNeeded[]` (and, optionally, its `judgmentAdvisory[]`),
and finalize via `judgments`. They never
re-evaluate a mechanical rule. `/ba-audit-pre-dev` keeps reading the written
verdicts unchanged.
