HumankindAssetFramework

Design note — pack pre-flight validator (third-party author DX)

Status: designed, not built. Scoped for the package-release phase. Raised by an external review (2026-08-02); the pack structure half already exists (see below). This note fixes what to validate, where the checks live, and the message format, so the eventual build is a straight implementation.

The gap this closes

Today a HAF pack fails in two very different ways depending on the mistake:

The validator turns silent content failures into named, actionable messages before (or at) load.

What to validate (per registry entry)

Grounded in the fields a ModelEntry / district / prop / projectile entry actually references:

Class Fields Check Where it’s checkable
Asset GUIDs skeleton, atlas, clip, moveClip/idleClip/… , district FxMesh, prop MeshCollection the GUID resolves to a loaded asset of the right kind boot-time (needs the game’s asset DB)
Bone names muzzleBone, turretBone, animateBones, hand-prop bone the named bone exists in the entry’s skeleton editor (baker knows the model’s bones) and boot-time (against the loaded skeleton)
File paths soundFile, soundStart/Stop/Idle/Attack/Death/Battle, PNG skins the file exists in the pack folder editor and boot-time (plain file existence)
Target unit pawnDescription matches a real game unit descriptor editor (pawn dropdown) and boot-time (unit catalog)
Schema field names, types, enums (materialMode), numeric ranges keys are known, types/enums valid, ranges sane editor (pre-ship)

Note the split: file/bone/pawn/schema checks are doable in the editor (before the pack ever ships — the best DX), while GUID resolution and bone-against-loaded-skeleton need the running game, so they belong to a boot-time pass on the end user’s machine.

Where the checks live (two surfaces, one core)

Factor the rules into one pure ValidateEntry(entry, context) returning a list of (severity, message) — mirrors the existing FormationOverridePatch.Validate(Entry) pattern and stays unit-testable (it’s pure logic over data + a small lookup interface). Two thin callers:

  1. Editor button — “Validate pack” in the pack/Factory window. Runs the checks it can without the game (files exist, bones exist in the inspected model, pawn is real, schema/enums/ranges). This is the pre-ship gate — an author sees problems before distributing.
  2. Boot-time pre-flight pass in the plugin, right after pack resolution and asset registration. Runs the checks that need the loaded game (GUID resolution, bone-against-actual-skeleton, file existence on the user’s disk) and appends a ## Pre-flight section to haf_load_report.txt plus a summary log line.

Message format

One line per problem, always naming pack + entry + the specific fault + (where cheap) the valid options:

[Preflight] pack 'coolmod' entry 'PanzerIV' (pawn 'AttackHelicopter'):
    bone 'Turrret' not found in skeleton — available: Root, Body, Turret, Barrel, Muzzle
    texture 'skins/panzer.png' not found in pack folder
    clip GUID 3f2a… did not resolve to a ClipCollection (was it baked?)

Phasing

Non-goals / relationship to existing tools