# EngineFilenameSchema Helper Module **Task**: AZ-281_engine_filename_schema **Name**: EngineFilenameSchema Helper **Description**: Implement the shared `EngineFilenameSchema` helper for the self-describing `.engine` filename schema (D-C10-7). TensorRT engines are NOT portable across `(SM, JetPack, TRT, precision)` tuples; encoding the tuple in the filename makes mismatch instantly visible at takeoff load (F2). Used by C7 (writes engines on compile, reads on `deserialize_engine`) and C10 (compiles engines via C7 and writes them to the cache root). Stateless static-only design. **Complexity**: 2 points **Dependencies**: AZ-263_initial_structure **Component**: shared.helpers.engine_filename_schema (cross-cutting; epic AZ-264 / E-CC-HELPERS) **Tracker**: AZ-281 **Epic**: AZ-264 (E-CC-HELPERS) ### Document Dependencies - `_docs/02_document/contracts/shared_helpers/engine_filename_schema.md` — frozen public interface this task produces. - `_docs/02_document/common-helpers/06_helper_engine_filename_schema.md` — design rationale (D-C10-7). ## Problem TensorRT engines are not portable. An engine compiled for SM 87 / JetPack 6.2 / TRT 10.3 / FP16 will fail to deserialize — or, worse, deserialize and silently produce wrong output — on a host with a different `(sm, jp, trt, precision)` tuple. Without a self-describing filename: - C7's `deserialize_engine` cannot tell whether an engine in the cache root matches the host capabilities until it tries to load it (an expensive, non-cheap, partially-side-effecting operation). - C10 has to maintain an out-of-band sidecar mapping filenames to tuples; that sidecar drifts. - An operator who copies an engine from a different deployment by mistake gets opaque "deserialize failed" errors at takeoff instead of a clear "engine was built for sm87, host is sm72". ## Outcome - A single `helpers.engine_filename_schema` module is the only path through which any onboard process composes or parses `.engine` filenames. - The schema makes `(model_name, sm, jetpack, trt, precision)` part of the filename: `{model}__sm{SM}_jp{JP}_trt{TRT}_{precision}.engine`. F2 takeoff load uses `matches_host` to decide which engines to deserialize and which to refuse before paying the deserialise cost. - The schema is strict — invalid model names, non-dotted version strings, unknown precisions are rejected at `build` time; malformed filenames are rejected at `parse` time. Both raise `EngineFilenameSchemaError` with messages that name the offending field. - Round-trip identity: `parse(build(*args)) == EngineCacheKey(*args)` for any valid args. Round-trip is the contract test that catches any future format drift. ## Scope ### Included - `EngineFilenameSchema` static methods: `build`, `parse`, `matches_host`. - `EngineFilenameSchemaError` exception type. - Public interface contract published at `_docs/02_document/contracts/shared_helpers/engine_filename_schema.md`. ### Excluded - Schema versioning (no `schema_version` field) — adding a new tuple dimension is a Plan-phase carryforward. - Engine compilation / compatibility resolution — C7. - Hot-loading / lazy materialisation — C7. - Filename collision detection across cache roots — C10's Manifest. - The `EngineCacheKey` / `HostCapabilities` types themselves — owned by `_types/manifests.py` (AZ-263). ## Acceptance Criteria **AC-1: Reference example builds correctly** Given `("ultravpr", 87, "6.2", "10.3", "fp16")` When `build` runs Then the result is exactly `"ultravpr__sm87_jp6.2_trt10.3_fp16.engine"` **AC-2: Round-trip identity** Given 10 random valid tuples When each round-trips through `parse(build(*args))` Then each produces deep-equal `EngineCacheKey` outputs **AC-3: Host-match exact** Given a filename built for `(sm=87, jp=6.2, trt=10.3)` and a `HostCapabilities(sm=87, jp=6.2, trt=10.3)` When `matches_host` runs Then the result is True **AC-4: Host-mismatch on any tuple element returns False (no exception)** Given a filename built for `(sm=87, jp=6.2, trt=10.3)` and a host with `sm=72` When `matches_host` runs Then the result is False (NOT an exception — tuple mismatch is the expected "not a match" path) **AC-5: Precision enum strictness** Given `build(..., precision="bf16")` When the call runs Then `EngineFilenameSchemaError` is raised mentioning the allowed enum `{fp16, int8, mixed}` **AC-6: Model-name character set** Given `build("UltraVPR", ...)` (uppercase letters) When the call runs Then `EngineFilenameSchemaError` is raised mentioning the allowed `[a-z0-9_]` set **AC-7: Reserved separator collision** Given `build("ultra__vpr", ...)` (double underscore in model name) When the call runs Then `EngineFilenameSchemaError` is raised mentioning the reserved `__` separator **AC-8: Version format strictness** Given `build(..., jetpack="6.2.1", ...)` (three-segment version) When the call runs Then `EngineFilenameSchemaError` is raised mentioning the dotted `.` format **AC-9: Parse rejects malformed filenames** Given `parse("not_an_engine_file.bin")` When the call runs Then `EngineFilenameSchemaError` is raised **AC-10: Parse requires `.engine` suffix** Given `parse("ultravpr__sm87_jp6.2_trt10.3_fp16")` (missing `.engine`) When the call runs Then `EngineFilenameSchemaError` is raised mentioning the required suffix **AC-11: No upward imports (Layer 1 invariant)** Given the helper module When a static-import check runs Then it imports ONLY from `_types`, `re`, and stdlib — no `gps_denied_onboard.components.*` imports anywhere ## Non-Functional Requirements **Performance** - No specific latency budget per `_docs/02_document/common-helpers/06_helper_engine_filename_schema.md` (consumers are pre-flight / takeoff-load). Sanity bound: each helper call ≤ 50 µs on Tier-2. **Reliability** - Pure deterministic; same input → byte-equal output. - `EngineFilenameSchemaError` is the ONLY exception type the public surface raises on validation / parse errors. ## Unit Tests | AC Ref | What to Test | Required Outcome | |--------|-------------|-----------------| | AC-1 | reference example | exact filename match | | AC-2 | round-trip 10 random valid tuples | deep-equal `EngineCacheKey` outputs | | AC-3 | matching host | True | | AC-4 | mismatched `sm` | False; no exception | | AC-5 | `precision="bf16"` | `EngineFilenameSchemaError`; mentions enum | | AC-6 | uppercase model name | `EngineFilenameSchemaError`; mentions `[a-z0-9_]` | | AC-7 | double-underscore model name | `EngineFilenameSchemaError`; mentions reserved separator | | AC-8 | three-segment version | `EngineFilenameSchemaError`; mentions dotted format | | AC-9 | malformed filename | `EngineFilenameSchemaError` | | AC-10 | missing `.engine` suffix | `EngineFilenameSchemaError`; mentions suffix | | AC-11 | importlinter / grep gate | no `components.*` imports | | NFR-perf | microbench each helper (10k iterations on Tier-2 fixture) | p99 ≤ 50 µs each | ## Constraints - Public surface frozen by `_docs/02_document/contracts/shared_helpers/engine_filename_schema.md` v1.0.0. - Layer 1 Foundation only. - Static-only design satisfies `coderule.mdc`. - No new dependency beyond what AZ-263 / E-BOOT pinned (only `re` and stdlib are needed). - The `EngineCacheKey` / `HostCapabilities` types live in `_types/manifests.py` (AZ-263 responsibility). ## Risks & Mitigation **Risk 1: A future format change breaks existing cache roots** - *Risk*: Adding a tuple dimension (e.g., `BUILD_*` flag combination) requires re-writing every existing `.engine` filename; deployments with stale cache roots fail silently. - *Mitigation*: The contract `Versioning Rules` mandate a major-version bump for any format change. C7's `deserialize_engine` should also reject unrecognised filename patterns rather than guess; that is C7's responsibility to wire on top of this helper's `parse`. **Risk 2: `matches_host` returns False without explanation** - *Risk*: An operator copies an engine from a different deployment; takeoff-load skips it; the operator sees "no engine matches host" without knowing which tuple element mismatched. - *Mitigation*: This helper is just the predicate. The error-surfacing UX is C7's / C10's responsibility — they call `parse` to extract the engine's tuple AND read `host_capabilities`, then format an actionable error. The contract documents the predicate's "True iff all tuple elements match" semantics so consumers can produce that message themselves. ## Runtime Completeness - **Named capability**: self-describing engine filename schema (D-C10-7 / `06_helper_engine_filename_schema.md`). - **Production code that must exist**: real format builder + parser + host-match predicate; real strict validation for all five tuple elements. - **Allowed external stubs**: none — pure string parsing on stdlib. - **Unacceptable substitutes**: `f"{model}_{sm}_{jp}_{trt}_{precision}.engine"` (single underscore separators ambiguate `model` from `sm`); silently truncating `jetpack="6.2.1"` to `6.2`; matching host with substring instead of exact-equality. ## Contract This task produces the contract at `_docs/02_document/contracts/shared_helpers/engine_filename_schema.md`. Consumers MUST read that file — not this task spec — to discover the interface.