# Dimension: Repo Structure

Evaluate whether `modules/<name>/` and `apps/<name>/` follow the canonical erp-kit shape at the filesystem level. This dimension is **binary** — either the conventions are met or they are not. There is no partial credit because filesystem rules (a file's name, whether a required directory exists) do not admit shades of grey.

This dimension is intentionally limited to the filesystem — naming, layout, and required directories. Code-content rules (e.g. "resolver should not import kysely directly") are NOT evaluated here; they live in the human review loop and can be added back as a separate dimension if real-world experience shows they need automation.

A single CLI does both checks. The naming rules are owned in-house (= an internal rule tree inside the framework, not an external linter), and required-directory checks live next to them. The agent runs one command and reads the JSON.

## Inputs

- `REPO_ROOT`
- `APP_ROOT` (optional)
- `MODULES_ROOT` (optional)

## Procedure

### 1. Run `measure structure`

```bash
npx erp-kit internal measure structure --app <APP_ROOT> --modules <MODULES_ROOT>
```

Internally this:

- Walks `modules/<name>/...` and `apps/<name>/...` with `fast-glob` and applies the bundled naming rule tree (= kebab-case directories, restricted file names at the module root, camelCase commands, etc.).
- Checks that required directories exist (= `db/`, `command/`, `query/`, `lib/` for modules; `backend/`, `frontend/` for apps).

If the command is unavailable (older erp-kit), surface `{ "severity": "info", "message": "measure structure command not available; structure dimension skipped" }` and exit the dimension.

The CLI returns JSON shaped like:

```json
{
  "modules": {
    "count": 5,
    "violations": [
      { "type": "missing-required-dir", "path": "modules/sales/lib" },
      { "type": "naming-violation", "path": "modules/sales/helpers.ts", "rule": ".ts:regex:^(module|index|tailor.config|vitest.config)$" }
    ]
  },
  "apps": {
    "count": 1,
    "violations": [
      { "type": "missing-required-dir", "path": "apps/x/frontend" }
    ]
  }
}
```

### 2. Interpret each violation

- `naming-violation` — file or directory name doesn't match the bundled convention (e.g. a `.ts` file at the module root that isn't `module/index/tailor.config/vitest.config`). The `rule` field describes which matcher failed.
- `missing-required-dir` — a module without `db/`, `command/`, `query/`, or `lib/` is broken in spirit. Same for an app without `backend/` or `frontend/`.

### 3. Evaluate (binary)

- **0 violations** → `status: "ok"`, no findings beyond an informational confirmation
- **≥ 1 violations** → `status: "violations"`, one finding per violation

Do not compute a numeric score. The dimension's role is to flag whether the filesystem is canonical, not to grade how canonical it is.

`severity` mapping for findings:

- naming-violation → `warning`
- missing-required-dir → `error`

### 4. Output

Return JSON with the structure-dimension shape:

```json
{
  "dimension": "structure",
  "status": "violations",
  "violationCount": 3,
  "findings": [
    {
      "severity": "error",
      "message": "modules/sales/lib is missing. Restore the directory and place generated/types files there."
    },
    {
      "severity": "warning",
      "message": "modules/sales/helpers.ts fails the module-root naming rule (only module/index/tailor.config/vitest.config allowed). Move helpers into the relevant command or rename per convention."
    },
    {
      "severity": "warning",
      "message": "apps/x/frontend is missing. The app shape requires a `frontend/` directory."
    }
  ]
}
```

When the repo is fully canonical:

```json
{
  "dimension": "structure",
  "status": "ok",
  "violationCount": 0,
  "findings": []
}
```

## Why no numeric score here

A continuous score (e.g. "structure: 72/100") would imply that 28 % of the structure is "kind of wrong" — but each filesystem violation is a discrete fix-or-don't-fix item, not a partial deficit. The binary status makes the next action explicit: either the list is empty (act on nothing) or it is not (fix every item). Pairing this with the continuous scores in templates / thinness gives the reader honest signal at the right resolution.

