feat(model+ui): F8-05 — race on OtherGroup, real attribution + N×M label
Tests · UI / test (push) Has been cancelled
Tests · Go / test (pull_request) Successful in 2m6s
Tests · Go / test (push) Successful in 2m6s
Tests · Integration / integration (pull_request) Successful in 1m51s
Tests · UI / test (pull_request) Successful in 3m53s

Issue #48 п.32 ("Stationed ship groups") shipped with a fragile race
fallback: when a foreign group sat on a non-`other`-kind planet the
inspector printed a generic "foreign" label, which collapsed the
race dropdown to a single uninformative bucket. The engine FBS
contract did not carry per-group race either, so live games hit the
same gap. This patch carries race authoritatively from the engine
through every layer down to the inspector.

Wire format & engine
- `pkg/schema/fbs/report.fbs`: add `race:string` to `OtherGroup` and
  `LocalGroup` (additive — old clients ignore).
- `pkg/schema/fbs/report/`: regenerated Go bindings.
- `ui/frontend/src/proto/galaxy/fbs/report/`: regenerated TS bindings.
- `pkg/model/report.OtherGroup.Race`: new field; carried through
  `LocalGroup` via the embedded `OtherGroup`.
- `pkg/transcoder/report.go`: encode + decode `race` on both
  `LocalGroup` and `OtherGroup`.
- `game/internal/controller/report.go.otherGroup`: set `v.Race`
  from `c.g.Race[c.RaceIndex(sg.OwnerID)].Name` so every emitted
  group — own or foreign — carries the resolved race name.

Legacy parser
- `tools/local-dev/legacy-report/parser.go`: capture the
  `<Race> Groups` header into `pendingOtherGroup.race`, fill local
  group `Race` from `p.rep.Race`, propagate both into the
  `report.OtherGroup` rows.
- Tests + smoke counts updated; regenerated `KNNTS{039,041}.json`
  fixtures so the synthetic loader carries the new field.

UI
- `ui/frontend/src/api/`: `ReportShipGroupBase.race` field;
  synthetic loader + FBS decoder populate it.
- `ui/frontend/src/lib/inspectors/planet/ship-groups.svelte`: the
  stationed-groups inspector picks race directly from
  `group.race` (own falls back to `localRace`, both finally to the
  `race.unknown` placeholder). The planet-owner / "foreign"
  heuristic is gone.
- Row label changes from "N ships mass M" to a compact
  `<class>` | `<N ×>` | `<mass>` three-column layout: the count
  cell is right-aligned tabular, the mass cell is right-aligned
  monospace + tabular, matching the inspector / calculator number
  conventions. Stale i18n keys removed
  (`ship_groups.row.count`, `.row.mass`, `.race.foreign`).
- All affected unit tests (8 files) carry the new `race` field.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Ilia Denisov
2026-05-27 16:23:17 +02:00
parent cc4bc3c2b7
commit 24c68e9846
26 changed files with 3601 additions and 1544 deletions
@@ -69,6 +69,7 @@ function localGroup(
mass: 12,
state: "In_Orbit",
fleet: null,
race: "Earthlings",
...overrides,
};
}
@@ -87,6 +88,7 @@ function otherGroup(
range: null,
speed: 0,
mass: 25,
race: "Klingons",
...overrides,
};
}
@@ -108,6 +108,7 @@ function localGroup(
mass: 12,
state: "In_Orbit",
fleet: null,
race: "Earthlings",
...overrides,
};
}
@@ -119,6 +119,7 @@ function group(
mass: 12,
state: "In_Orbit",
fleet: null,
race: "Earthlings",
...overrides,
};
}
@@ -104,6 +104,7 @@ function group(
mass: 25,
state: "In_Orbit",
fleet: null,
race: "Earthlings",
...overrides,
};
}
@@ -79,6 +79,7 @@ function localGroup(
mass: 12,
state: "In_Orbit",
fleet: null,
race: "Earthlings",
...overrides,
};
}
@@ -158,6 +159,7 @@ describe("ship-group inspector", () => {
range: null,
speed: 0,
mass: 50,
race: "Klingons",
};
const selection: ShipGroupSelection = { variant: "other", group };
const ui = render(ShipGroup, { props: { selection, planets: PLANETS } });
@@ -45,6 +45,7 @@ function localGroup(overrides: Partial<ReportLocalShipGroup> & Pick<ReportLocalS
mass: 1,
state: "In_Orbit",
fleet: null,
race: "Earthlings",
...overrides,
};
}
@@ -79,6 +79,7 @@ function makeLocalShipGroup(
mass: 0,
state: "InOrbit",
fleet: null,
race: "Earthlings",
...overrides,
};
}
@@ -97,6 +98,7 @@ function makeOtherShipGroup(
range: null,
speed: 1,
mass: 0,
race: "Klingons",
...overrides,
};
}
@@ -78,6 +78,7 @@ describe("reportToWorld — ship groups", () => {
mass: 12,
state: "In_Orbit",
fleet: null,
race: "Earthlings",
},
],
}),
@@ -112,6 +113,7 @@ describe("reportToWorld — ship groups", () => {
mass: 50,
state: "In_Space",
fleet: null,
race: "Earthlings",
},
],
}),
@@ -237,6 +239,7 @@ describe("reportToWorld — ship groups", () => {
origin: null,
range: null,
speed: 0,
race: "Earthlings",
mass: 1,
state: "In_Orbit",
fleet: null,