# Error Catalog Strategy

This document defines how Shellops identifies, structures, surfaces, and grows compile-time errors and warnings. It complements feature specs: feature pages should reference the error codes they introduce; this doc defines the cross-cutting rules.

## Goals

- Make errors **self-documenting** and easy to search in docs and source.
- Keep a **stable catalog** with hex-coded ranges that scales predictably.
- Support **human** and **machine** consumers equally (`--explain`, `--json`).

## Error identification

### Hex codes with human-readable keys

Every error has two identifiers:

1. **`code`** — hex identifier in the form `e###` (e.g. `e101`). Fixed-width, sortable, greppable.
2. **`key`** — kebab-case human-readable string (e.g. `unresolved-import`). Descriptive, searchable.

Both are always present. `code` is the primary machine identifier; `key` is the primary human identifier. Either can be used for suppression, filtering, and matching.

### Code format: `<c>###`

Two prefixes: `e` for user/consumer errors, `E` for engine/shellops errors. Three hex digits (`000`–`FFF`) identify the specific error.

| Prefix | Owner |
|--------|-------|
| `e` | User / consumer |
| `E` | Engine / shellops internals |

`e000` is the generic user throw. Engine codes are assigned as features land. The `origin` field on the error object (auto-populated) carries detail about where the error originated (syntax, resolve, runtime, etc.) — the code itself just distinguishes user vs engine.

### Key naming convention

- **kebab-case**
- **Descriptive** and **concise** (prefer `unresolved-import` over `import-bad`).
- **<32 characters**, no spaces.

### Examples

| Code | Key | Origin | Typical situation |
|------|-----|--------|-------------------|
| `E001` | `unresolved-import` | syntax | Import path does not resolve |
| `E002` | `unclosed-string` | syntax | String literal missing closing delimiter |
| `E003` | `unexpected-token` | syntax | Token not valid in current parse context |
| `E004` | `type-mismatch` | type | Value does not match expected type |
| `E005` | `duplicate-enum` | syntax | Same enum name declared twice |
| `E006` | `missing-dependency` | resolve | Required dependency not satisfied |
| `E007` | `duplicate-export` | resolve | Same name exported twice |
| `E008` | `unused-import` | resolve | Import never used (warning) |
| `E009` | `deprecated-syntax` | syntax | Construct deprecated, still accepted with warning |
| `E00A` | `ambiguous-env` | resolve | Multiple ops declare same env key without explicit `env = {}` |
| `E00B` | `posix-violation` | emit | Emitted code contains non-POSIX construct |

## Error structure

Every emitted error uses the `@error` type — the same structure used by DSL-level `throw`:

### Required fields

- **code** — hex identifier (`e###`).
- **key** — kebab-case human-readable identifier.
- **message** — short human-readable explanation.

### Auto-populated fields

- **location** — `file:line:col` for the primary span.
- **stack** — structured error stack (appended on rethrow).

### Optional fields

- **context** — structured key-value metadata (e.g. the actual vs expected type, the import path tried).

### Source context

Diagnostics include a **source snippet** with an **arrow** naming the relevant span:

```text
error unresolved-import (E001):
at ci.ops:3:6:

   |
 2 | @use std.log as log
 3 | @use ./validaton as validate  <--- The issue is `./validaton`
 4 |
   |

Cannot find module 'validaton'. Did you mean './validation'?
```

The format includes both the hex code and the key for maximum searchability. See [error-display.md](./error-display.md) for the full rendering spec.

## `--explain`

```text
ops-engine --explain <code-or-key>
```

Accepts either the hex code (`e101`) or the key (`unresolved-import`). Prints expanded explanation: what the error means, common causes, and typical fixes.

## Machine-readable format

When **`--json`** is enabled, errors emit structured JSON matching the `@error` schema. See [machine-output.md](./machine-output.md) for the full batching and streaming spec.

Minimal per-error object shape:

```json
{
  "code": "E001",
  "key": "unresolved-import",
  "message": "cannot find module 'validaton'",
  "help": "did you mean './validation'?",
  "origin": "syntax",
  "location": { "file": "ci.ops", "line": 3, "col": 6 }
}
```

## Catalog growth

- **New errors** are added as features land; the catalog **grows with tiers**.
- Each **feature page** should list the **error codes** that feature introduces or changes.
- The **authoritative catalog** is maintained as a **flat data file** alongside engine source, avoiding scattered ad-hoc strings. The current implementation keeps it at `engine/errors/error_catalog.en.txt` and embeds it with `include_str!` so `opc`, `opx_error.lib.sh`, and future standalone compiled pipelines can share the same catalog text without requiring a runtime file lookup.
- The catalog should grow from short `*.help` entries into full explanations using multi-line `*.explain` entries:

```text
catalog.<code>.explain = """
This error means...

Detailed examples of when and why <code> is hit, how to resolve it,
and what the resolution looks like.
"""
```

The `errsplain`/`--explain` surface should prefer `*.explain` when present, then fall back to `*.help`.

## Warning levels

### Errors

- **Severity:** blocks successful compilation.
- Must be **fixed** (or explicitly suppressed only if policy allows and is documented).
- Errors use `E`-prefixed codes (engine) or `e`-prefixed codes (user).

### Warnings

- **Severity:** compilation **continues**; output is non-zero exit if warnings-as-errors is enabled.
- Typical cases: unused imports, dead groups, plausible typos, unused env keys, deprecated syntax.
- Warnings use the same code ranges and structure as errors; severity is metadata, not a separate system.

### Requirement provenance

When the graph references **environment variables** or other requirements, missing or unmet requirements trace back to the **consuming op** and **source line** where the requirement is introduced, so users do not hunt through indirect callers.

---

*Cross-reference: structured batch output and streaming behavior — [machine-output.md](./machine-output.md). Error object shape and DSL throw forms — [error-handling.md](../features/error-handling.md).*
