From e61507a6021e4eed9efa06c2b873d4d97205ea3f Mon Sep 17 00:00:00 2001 From: "@permadeath.com" Date: Sat, 22 Aug 2026 11:30:47 -0400 Subject: [PATCH] fix(unit-search): the index keeps the header headquarters-api reads Moving `megamek` into the build block would have had that service refuse the index and fall back to drawing silhouettes for every design. It is written in both places until the service reads the build, and TODO says so. Change-Id: Ia0e85fb1cb781415e26895cc8156ec5173d616e6 --- TODO.md | 21 +++++++++++++-------- crates/helm-cli/src/build.rs | 2 ++ crates/helm-query/src/facets.rs | 13 +++++++++++++ 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/TODO.md b/TODO.md index 678f9e9..f8f7611 100644 --- a/TODO.md +++ b/TODO.md @@ -17,7 +17,7 @@ problem, never code. ## What to do next, in order -Twenty-one things, ranked. The tiers are the argument: the first finishes what +Twenty-two things, ranked. The tiers are the argument: the first finishes what `plan/unit-rules.md` already promises, the second widens what helm can score at all, and the third is what makes any of it reach a player. Everything below appears again in its own section with the detail; this is the order to take @@ -83,29 +83,34 @@ to helm. in `helm-query`, which helm-mcp and the boundary both read. What is left is headquarters': `web/src/units.ts` still has a filter table of its own, and that is the second implementation the epic warns about. -15. **An identity for the catalogue.** `units.json` and `loadout.json` carry a +15. **Drop the index's duplicated header.** `units.json` writes `megamek` and + `helm` at the top level as well as inside `build`, because + headquarters-api refuses an index it cannot find a top-level `megamek` in + and falls back to silhouettes. Delete both once + `services/api/src/units.rs` reads `build`. +16. **An identity for the catalogue.** `units.json` and `loadout.json` carry a `build` block and refuse to be joined across runs; `catalogue.jsonl` is JSON Lines with nowhere to put one, and is keyed only by the prefix it is published under. Two dumps of one MegaMek release are not interchangeable - see the note in CLAUDE.md - so the prefix is not enough. -16. **The redistribution question.** MegaMek's data is CC BY-NC-SA and serving +17. **The redistribution question.** MegaMek's data is CC BY-NC-SA and serving an index of it to browsers is redistribution. It needs a decision and an attribution mechanism *before* 13 ships. -17. **Provenance on every record.** Which MegaMek, which producer, which +18. **Provenance on every record.** Which MegaMek, which producer, which `RULES_VERSION`. The constants exist and nothing writes them into the database or into an ATProto record, so a stored figure cannot be told from one computed against a different MegaMek. -18. **The attribution and the repair list on a match page.** +19. **The attribution and the repair list on a match page.** `plan/after-action.md`. The pieces are built and cross the wasm boundary; what is missing is the screen that reads them. -19. **Damage that persists between matches.** `plan/campaign.md`. The +20. **Damage that persists between matches.** `plan/campaign.md`. The mechanical half exists; storing a force's condition as a record and reading it back is the rest, and the construct-write-read round trip supports it. -20. **Formation validation.** `list_formations` and `validate_formation` +21. **Formation validation.** `list_formations` and `validate_formation` against the Campaign Operations blueprints - "is this a legal Fire Lance". Validation before generation, and it needs no faction data: it is role and count, both of which helm holds. -21. **Ingest `data/forcegenerator/`.** 40 era files of per-faction availability +22. **Ingest `data/forcegenerator/`.** 40 era files of per-faction availability weights, `factions.xml`, and 61 faction rulesets that are a small DSL of their own. This is the foundation for force generation, faction filtering and era-correct scenarios, and nothing above it should be attempted until diff --git a/crates/helm-cli/src/build.rs b/crates/helm-cli/src/build.rs index 15f1ba8..037f87e 100644 --- a/crates/helm-cli/src/build.rs +++ b/crates/helm-cli/src/build.rs @@ -443,6 +443,8 @@ pub(crate) fn index(args: &[String]) -> Result<(), String> { } let mut document = helm_query::Index { + megamek: build.megamek.clone(), + helm: build.helm.clone(), build: build.clone(), sprite_base: "data/images/units".to_string(), counts, diff --git a/crates/helm-query/src/facets.rs b/crates/helm-query/src/facets.rs index 96c4eb7..40a0448 100644 --- a/crates/helm-query/src/facets.rs +++ b/crates/helm-query/src/facets.rs @@ -137,6 +137,17 @@ pub struct Counts { #[derive(Serialize, Deserialize)] pub struct Index { pub build: Build, + /// The release, again, at the top level. + /// + /// Where it was before there was a build block, and where headquarters-api + /// still reads it: that service refuses an index it cannot find a + /// `megamek` in and falls back to drawing silhouettes. Written until it + /// reads `build.megamek` instead, and no longer. + #[serde(default, skip_serializing_if = "String::is_empty")] + pub megamek: String, + /// The same, for the helm build. See `megamek` above. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub helm: Option, /// Relative to the release, and the prefix a sprite path is joined to. /// The bucket holds the art tree at this path under the same version, so /// a consumer needs no second convention to turn one into an address. @@ -242,6 +253,8 @@ mod tests { fn document(names: &[&str]) -> Index { Index { build: build(names), + megamek: "0.51.0".into(), + helm: None, sprite_base: "data/images/units".into(), counts: Counts::default(), units: names -- 2.51.2