# Common Helper — `EngineFilenameSchema`

## Purpose

Self-describing `.engine` filename schema per D-C10-7. TensorRT engines are NOT portable across `(SM, JetPack, TRT, precision)` tuples; the filename schema makes mismatch instantly visible at takeoff load (F2) so refusing-to-deserialize-on-mismatch becomes trivial.

## Used By

- C7 — Inference Runtime (writes engines with this schema; reads them on `deserialize_engine`).
- C10 — Pre-flight Cache Provisioning (compiles engines via C7 with this schema; writes them to the cache root).

## Interface (sketch)

```
class EngineFilenameSchema:
    @staticmethod
    def build(model_name: str, sm: int, jetpack: str, trt: str, precision: str) -> str
    @staticmethod
    def parse(filename: str) -> EngineCacheKey
    @staticmethod
    def matches_host(filename: str, host_capabilities: HostCapabilities) -> bool
```

Filename format: `{model}__sm{SM}_jp{JP_dotted}_trt{TRT_dotted}_{precision}.engine`

Example: `ultravpr__sm87_jp6.2_trt10.3_fp16.engine`

```
EngineCacheKey:
  model_name:       string
  sm:               int               (e.g. 87 for Jetson Orin Nano Super)
  jetpack:          string            (e.g. "6.2")
  trt:              string            (e.g. "10.3")
  precision:        enum {fp16, int8, mixed}

HostCapabilities:
  current_sm:       int
  current_jetpack:  string
  current_trt:      string
```

## Implementation Notes

- Stateless; pure string parsing.
- `matches_host` returns true iff every tuple element matches exactly. F2 takeoff load uses this to decide which engines to deserialize and which to refuse.

## Caveats

- The dotted-version format must round-trip cleanly through filesystems (no `/` or `\` in dotted versions; safe).
- Adding a new tuple dimension (e.g., a per-binary `BUILD_*` flag combination) requires extending the schema AND every existing `.engine` filename. Versioning the schema itself is a Plan-phase carryforward if/when needed.

## Cycle-1 operational reality

The shipped surface in `src/gps_denied_onboard/helpers/engine_filename_schema.py` (AZ-281) is stateless and static-only, with a single compiled regex governing `parse` and `build`. The host-match predicate compares `(sm, jetpack, trt)` exactly; **precision is NOT part of the host match** (a `fp16` engine and an `int8` engine for the same SM/JetPack/TRT both "match the host" — the takeoff-load verifier picks the one it wants by precision separately).

- **`EngineFilenameSchemaError`** — single public exception type (subclasses `ValueError`). Raised on: non-`str` inputs to `build` / `parse`; missing `.engine` suffix; regex non-match; reserved `__` separator inside `model_name`; `model_name` outside `[a-z0-9_]+` or longer than 64 chars; `sm` not a non-bool positive int; version not matching `\d+\.\d+`; precision not in `ALLOWED_PRECISIONS`.
- **`ENGINE_SUFFIX = ".engine"`** — public module-level constant.
- **`ALLOWED_PRECISIONS = frozenset({"fp16", "int8", "mixed"})`** — public module-level constant; exposed so C7's takeoff-load decision tree and C10's engine-build orchestration can validate operator-supplied precision without hard-coding the enum.
- **Strict model-name validation** — `[a-z0-9_]+`, non-empty, ≤64 chars, no embedded `__` (reserved as the model/SM separator). Catches "operator typed a model name with a hyphen" before any filesystem operation runs.
- **Strict version validation** — both `jetpack` and `trt` must match dotted `<major>.<minor>` (e.g. `"6.2"`, `"10.3"`). Patch components are deliberately NOT supported in the filename — the engine ABI is stable within `<major>.<minor>` per the JetPack/TRT release notes.
- **`sm` validation** — must be a non-bool `int > 0`. Python's `bool ⊆ int` quirk would otherwise let `True` slip through as `sm=1`.
- **`matches_host` ignores precision by design** — the filename's `precision` segment is informational for the host-match check. C7's `deserialize_engine` uses `matches_host` to filter "engines this host can run at all" before applying its own precision policy.

### Cycle-1 task lineage

- AZ-281 — initial helper, contract producer.
- No cycle-1 follow-up tasks touched this helper. C7's `deserialize_engine` (AZ-301) and C10's engine compiler (AZ-321) consume it without extension.
