# Build model + MBD import/export

Internal build representation and the codec for Mids Reborn's "MBD" build-share
format (the string you get from Mids' "Export build" / can paste into
"Import build"). This is what lets someone bring in a build they've already
made in Mids Reborn instead of re-entering it by hand, and connects that
build straight into the calculation engine (`../calc-engine/`).

## Modules

- **`mbd_format.py`** - the wire-format codec. Mirrors
  `MidsReborn/Core/Utils/DataClassifier.cs` (header detection),
  `MidsReborn/Core/BuildFile/BuildManager.cs`'s `ValidateAndLoadImportData`
  (the actual header/body split + decode), `MidsReborn/Core/Utils/Compression.cs`
  (base64 + Brotli), and `ModernZlib.BreakString`/`UnbreakString` (the
  67-char, `|`-bookended line wrapping). Handles the standard `|MBD;...;|`
  format, a bare-base64 payload with no header (Mids' "UnkBase64" case), and
  explicitly rejects the legacy pre-Brotli `|MxDz;...;|` format (documented
  as out of scope - only needed for very old forum-shared builds). Also
  encodes, i.e. can produce a Mids-importable string from our own build data.
- **`brotli_ctypes.py`** - a small ctypes shim calling the system's
  `libbrotlidec`/`libbrotlienc` shared libraries directly. There's no `brotli`
  Python package installable in this sandbox (no PyPI network egress for it),
  but `libbrotli1` is already present at the OS level, so this just binds the
  one-shot `BrotliDecoderDecompress`/`BrotliEncoderCompress` C functions -
  exactly the one-shot (non-streaming) usage Mids' own `BrotliStream` calls
  need to match. **This is a cloud-prototype-environment workaround, not a
  design choice worth carrying into the real web app** - a browser has
  `CompressionStream`/`DecompressionStream` with native Brotli, and Node.js
  has Brotli built into its `zlib` module, so a JS/TS implementation of this
  same codec needs no external dependency at all.
- **`build_model.py`** - the internal build representation itself: Python
  dataclasses (`CharacterBuild`, `PowerData`, `SlotData`, `EnhancementData`,
  `SubPowerData`) mirroring `MidsReborn/Core/BuildFile/DataModels/*.cs`
  field-for-field, so decode -> re-encode round-trips losslessly. Also:
  - `all_slotted_enhancement_uids(build)` / `power_slotted_enhancement_uids(entry)`
    - flatten a build (or one power) down to the enhancement UIDs the calc
    engine's `slot_math`/`set_bonus_calc` modules expect.
  - `resolve_build(db, build)` - cross-references every archetype/powerset/
    power/enhancement reference in a decoded build against the parsed
    Homecoming database, returning a report of what resolved and what
    didn't. This doubles as our validation method (see below).

## Validation

`test_build_model.py` builds a synthetic build out of *real* identifiers
pulled from the actual parsed database (a Blaster slotted with 5 Decimation
pieces + an Achilles' Heel proc in Fire Blast, plus a Luck of the Gambler
+recharge piece in a Leadership toggle), then:

1. Encodes it to MBD text and decodes it back, and asserts the result equals
   the original build exactly (lossless round trip through Brotli+base64+the
   line-wrapped header).
2. Runs `resolve_build` against the real database and asserts zero
   unresolved references. This is the closest thing we have to "validate
   against known Mids Reborn output" without a live copy of the app to diff
   a real export against: every reference in a build has to resolve against
   the exact same database Mids itself ships, so this genuinely catches
   schema mistakes, not just internal self-consistency.
3. Feeds the decoded build straight into `slot_math.aggregate_power_slots`
   and `set_bonus_calc.compute_set_bonuses`, confirming the whole
   import -> calculation pipeline chains together (Decimation's known
   +6.25% recharge and Luck of the Gambler's +7.5% both come out correctly
   from a build that arrived via the MBD codec, not hand-fed UIDs).
4. Exercises the bare-base64 decode path and error handling (legacy MxDz
   rejection, garbage input, compressed-size mismatch).

All of the above pass as of this writing.

## Known gaps / not yet handled

- **Legacy `MxDz` format** (pre-Brotli, custom zlib + hex) - deliberately
  unsupported; only matters for very old forum-shared builds.
- **`SubPowerEntries`** (pet/summon sub-powers) round-trip through the model
  but aren't yet resolved/validated by `resolve_build` or fed into the calc
  engine - not needed until the build model handles summoned entities.
- **`BuiltWith` metadata** (`App`/`Version`/`Database`/`DatabaseVersion`) is
  kept as an opaque passthrough dict rather than strictly typed - it's
  metadata only, never consumed by calculations, and .NET's `Version` type's
  exact JSON shape wasn't worth chasing down for that.
- The **`Enhancement` legacy fallback field** some very old exports used
  (a raw enhancement display name instead of a stable `Uid`, per
  `EnhancementDataConverter.cs`) isn't implemented - current-era exports all
  carry `Uid` directly.
