--- id: unit-library title: Every unit MegaMek knows is queryable, from any MegaMek status: open repos: [helm] dependsOn: [] exitCriterion: > helm builds its outputs from an unmodified MegaMek install, of a version given to it rather than baked in, committing none of MegaMek's data — and says how far the result agrees with MegaMek. --- # unit-library [helm](https://tangled.org/lance.blue/helm) reads MegaMek's unit library and writes an indexed SQLite database of it: 10,988 units with tonnage, tech base, era, movement, armour, loadout, quirks and critical slots. That much works. What it does not yet do is stand on its own. Three things are still borrowed. **Battle value, cost and the Alpha Strike conversion are computed, not declared.** They appear in no `.mtf` or `.blk` file — MegaMek derives them when it loads a design. helm gets them today from `bridge/`, two Java programs that read `MekSummaryCache`. That is honest and it works, but it means a build needs a JDK container and it means those columns are MegaMek's answer rather than one we can check. **The equipment catalogue comes from the same place.** 4,348 entries with heat, damage, ranges and tonnage. This is the hidden dependency: computing battle value ourselves needs the catalogue first, so the catalogue is the real prerequisite for [unit-rules](unit-rules.md), not the battle value formula. **Nothing says how far we agree with MegaMek.** The database has one figure per unit and no record of where it came from or whether it is right. ## What this epic is for The unit data has three consumers coming — [unit-search](unit-search.md), [unit-rules](unit-rules.md) and [forces](forces.md) — and each of them wants the library to be a value they are handed, not a thing they go and find. That shape has to exist before they are built on it. There are **two outputs**, not one. The database is the server's and the agent's; a reduced index is the browser's, for the force-building screen. Both are generated from the same read of the same install, so neither can be a version behind the other. See [unit-search](unit-search.md) for what the index has to carry. A unit's identity across all of this is **chassis and model, as two fields**, because that is what a `.mul` records and so what [forces](forces.md)'s `blue.lance.force` and any export must carry. The display name is `chassis (clanname) model` and is not a key; the MUL id is not one either, since 153 of 4,279 Meks have none. - [ ] **Take the MegaMek version as an input, not a constant.** helm should read whatever install it is pointed at and stamp every artefact with the version it came from. Deciding which version the *runtime* uses is a separate question and a real one: arena pins 0.51.0, so a battle value shown to a player from a newer MegaMek disagrees with the number the game gives them. - [ ] **Commit none of MegaMek's data.** The catalogue and the units are read from the install at build time. It stays CC BY-NC-SA content living in MegaMek's tree, not ours, which is the same reason the built `.sqlite` is gitignored. - [ ] **Record provenance.** Which MegaMek, which producer, which rules version. Without it a conformance failure is indistinguishable from a database built against a different MegaMek, which is exactly the ambiguity you hit while chasing a deviation. - [ ] **Run the boundary check automatically.** The crates are split and `scripts/check-boundaries.sh` encodes both rules, but helm has no commit hooks and no CI, so nothing runs it. A check somebody has to remember is the comment it was meant to replace. - [ ] **Build the conformance harness.** Run both producers over the same library and diff. It reports zero agreement until [unit-rules](unit-rules.md) starts, and that is the point — it is the scoreboard that work is scored against, and it doubles as the drift detector when MegaMek releases. - [ ] **Register deviations rather than remembering them.** `crates/mms` already does this for scenario files: `Quirk::UnreadFactionKey` and its siblings record where MegaMek reads less than its own format says, as data, in the parser. The same shape here — each entry naming the units affected, both answers, the argument from the rules text, and an upstream link. The test asserts the set exactly, so a new unexpected difference fails, and one that quietly disappears because they fixed it fails too. ## The first deviation is already known `ASTurretSummary.getSpecialsDisplayString()` throws on certain turreted units. It cost the Alpha Strike special abilities of 967 units, 887 of them Tanks, and it took down the first dump entirely before the walk over MekSummary was made defensive. It is the template for what the registry holds. ## Done - [x] **The computed values are a type, not a source.** `ComputedStats` lives in `helm-core` — battle value, cost and the Alpha Strike figures as a plain value that `helm-bridge` fills today and `helm-bv` fills later. `helm-db` consumes it and never learns which produced it, which is what keeps it free of a battle value dependency and what makes the conformance harness a diff between two of these rather than bespoke code. - [x] **The crates are split.** `helm-core` carries the types and the catalogue and does no I/O; `helm-facet` and `helm-force` sit beside it, all three clear of `rusqlite`, `zip` and `serde_json` so they can reach wasm. `scripts/check-boundaries.sh` states both rules and checks them — wiring it to something that runs is the open item above. - [x] **The repo exists and reads both formats.** `helm-unitfile` parses `.mtf` and `.blk` directly — MegaMek's two unit formats, neither of them XML despite one looking like it. All 10,988 files in the 0.51.0 library parse with no failures. The awkward parts are covered by tests rather than discovered later: the `Weapons:N` count block, crit sections, `source:TR:3039` keeping its own colon, and blank lines inside a `.blk` block body. - [x] **A database with the computed columns in it.** `helm-db` writes units, loadout, criticals, armour by location, quirks and the equipment catalogue, indexed, with full-text search over names. Every one of the 10,988 units carries battle value, cost and Alpha Strike figures. - [x] **Two naming traps found and handled.** A Clan dual-named design is `chassis (clanname) model` — `Black Hawk (Nova) Prime` — and 434 units have one; treating the chassis as the whole name silently lost every one of them. And `.blk` spells unit types unspaced (`GunEmplacement`) where MekSummary spaces them (`Gun Emplacement`), so a filter written against MekSummary's spelling excluded nothing at all.