# Diagnostics

Every Astrale diagnostic uses the engine-neutral `LintDiagnostic` model and contains:

- A stable `astrale/<slug>` identifier.
- `error` or `warning` severity.
- A message describing the violated invariant.
- Remediation and a documentation URL when available.
- A normalized source location when one exists.
- The policy owner: `sdk`, `oxlint`, or `analyzer`.

Messages describe domain policy rather than AST or analyzer implementation details. Engine changes must
not rename identifiers, suppressions, or output fields.

## Output

Stylish output is intended for people. JSON output preserves the complete `LintResult` for automation.
Diagnostics are sorted by path, position, identifier, and message. Duplicate reports with the same
identifier and source span are collapsed because dual ownership is a tooling defect.

Exit codes are part of the contract:

- `0`: no enabled error remains.
- `1`: policy diagnostics failed the run.
- `2`: configuration, dependency resolution, or an underlying tool failed.

Project preflight failures are tool errors, not suppressible policy diagnostics. Their messages must
name the conflicting package or tool and preserve the original cause.

## Suppressions

Astrale rules share one exact, engine-neutral suppression syntax:

```ts
// astrale-disable-next-line astrale/raw-path-only-at-boundary -- provider cache key requires a string
const key = node.path.raw
```

A suppression must:

- Name exact rule identifiers.
- Include a non-empty reason after `--`.
- Apply only to the following line.
- Continue to work if rule ownership moves between Oxlint and the analyzer.

Generic Oxlint rules continue to use Oxlint suppression syntax. Suppressions are exceptional evidence,
not compensation for an ambiguous or noisy rule.
