Owner-reported polish on top of #48, plus a legacy-parser gap that
prevented verifying stationed ship groups against a real .REP fixture.
UI:
- Production: drop the empty `(production)` placeholder option. Owned
planets always produce something, so the primary select now opens on
`industry` by default when `planet.production` is null/unknown,
keeping the row inside the four real production kinds at all times.
- Production: lock the row to a single line (no flex-wrap) and strip
border + padding from the ✓/✗ buttons so the apply/cancel icons read
as glyphs and the row no longer breaks into two visual rows for
Research / Ship contexts where both selects are present.
- Cargo routes: the placeholder option is now an `<option disabled>`
styled like a section header (greyed, italic) and reads "manage
routes" instead of "cargo routes". The wording shifts the intent
from a section label to an action prompt.
Legacy parser:
- F8-05 (#48 п.32) "Stationed ship groups" couldn't be verified against
the dg fixture because the legacy `<Race> Groups` blocks (outside
battles) and the `Unidentified Groups` block were dropped by the
parser — both are now wired up. Foreign group rows parse the
`# T D W S C T Q D P M` columns and resolve the destination against
the parsed planet tables (rows with an invisible destination drop,
matching the existing local-group convention). The legacy row
carries no origin / range columns, so foreign groups surface as
stationed at the destination.
- Smoke tests on every fixture extended with `otherGroups` and
`unidentifiedGroups` counts. New focused unit test
`TestParseOtherAndUnidentifiedGroups` covers the column layout, the
drop-on-unknown-destination rule, and the `X Y`-only unidentified
rows.
- `tools/local-dev/reports/dg/KNNTS039.json` and
`tools/local-dev/reports/dg/KNNTS041.json` regenerated so the
synthetic-loader fixtures carry the new arrays.
- README updated: the two sections move out of "Skipped sections" into
a "Foreign and unidentified groups" block; package doc-comment
reflects the broader scope.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The parity rule from ui/PLAN.md says every UI phase that decodes a
new Report field must extend the legacy converter in lockstep.
Phase 19 brings ship groups (LocalGroup / OtherGroup /
UnidentifiedGroup / IncomingGroup) and LocalFleet onto the wire-
compatible UI surface; this commit teaches
tools/local-dev/legacy-report to populate the three sections that
exist in the legacy text format:
- "Your Groups" → []LocalGroup. Cargo type, load, fleet name,
state, on-planet vs hyperspace position (origin / range) all
decoded; LocalGroup.ID is synthesised deterministically from
the per-report group index so re-running the converter
produces byte-identical JSON. Speed is left zero — the legacy
table doesn't expose it.
- "Your Fleets" → []LocalFleet. Origin / range / state mirror
the row layout used by Killer / Tancordia variants; gplus's
state-less rows still resolve.
- "Incoming Groups" → []IncomingGroup. Origin / destination
names — and `#NN` by-id references — resolve against the
parsed planet tables. Because the section can land before
"Your Planets" in some engines, group / fleet / incoming rows
are buffered and resolved in `parser.finish` after every
planet is known.
Battles, OtherGroup (only ever in battle rosters), and
UnidentifiedGroup stay out of scope — README.md spells out what
remains not-derivable.
Adds Killer031–033 / TSERCON_Z032–033 / Tancordia036–039 fixtures
to the dg directory and exercises three of them through new
TestParseDg{Killer031,Tancordia037,KNNTS041} smoke tests, plus
inline tests for each new section parser. Drops the stale
KNNTS039.json artefact left over from Phase 18 development.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>