# `src/validators` — local flow validator suite

Ported verbatim from `appmixer-skills/skills/generate-e2e-flows` (dispatcher +
22 validators + shared libs) so skills need no scripts of their own, and grown
here since (29 rules today). Exposed via
`appmixer flow validate <file|dir> [more...]`.

## Layout

- `rules/` — one ESM module per validator (`export const name/description`,
  `export const run(ctx)`). The nested `rules/package.json` sets
  `"type": "module"` so the CommonJS CLI loads them with dynamic `import()`.
  Auto-discovery: any `rules/*.js` not starting with `_` is a validator —
  adding a rule is just adding a file.
- `rules/lib/` — shared helpers (`flowutil.js`, `structural.js`, `coverage.js`).
- `flow-schema.json` — Ajv schema used by the `flow-schema` validator.
- `test-flow-template.json` — the canonical E2E flow template (passes all rules).
- `index.js` — CJS runner: target resolution (files / `test-flow-*.json` dirs),
  connectors-dir resolution, ruleset filtering, structured report.

## Rulesets

- `e2e` (default) — all rules, including E2E-specific ones
  (ProcessE2EResults wiring, assert specificity, coverage, determinism).
- `basic` — only generic flow rules (see `BASIC_RULES` in `index.js`).

## Connectors dir

Coverage rules (`component-coverage`, `input-coverage`, `inport-key-match`,
`outport-exists`, ...) need connector sources to load `component.json` schemas.
Resolution order: `--connectors-dir` option → `$APPMIXER_SKILL_CONNECTORS_DIR/src`
→ derived from the validated path (`<repo>/src/appmixer/...`). When unresolved,
those rules skip with a warning — validation still runs offline.

## `--json` report shape

```json
{
    "ok": false,
    "ruleset": "e2e",
    "connectorsDir": "/path/or/null",
    "flows": ["/abs/path/test-flow-x.json"],
    "validators": [
        {
            "name": "component-id-uuid",
            "description": "…",
            "failures": [{ "file": "test-flow-x.json", "message": "…" }],
            "warnings": [{ "file": "test-flow-x.json", "message": "…" }]
        }
    ],
    "totals": { "failures": 1, "warnings": 3 }
}
```

Exit code: 1 when `totals.failures > 0`, otherwise 0 (warnings do not fail).

## Keeping semantics identical

Validator names, messages and severities are unchanged from the skills repo, so
existing skill prompts referencing them keep working during the migration
(epic #392, sub-issue #391).
