Skip to content

feat: model NewRecruit schema additions over BattleScribe v2.03 - #308

Merged
amis92 merged 1 commit into
mainfrom
feat/newrecruit-schema-additions
Jun 23, 2026
Merged

feat: model NewRecruit schema additions over BattleScribe v2.03#308
amis92 merged 1 commit into
mainfrom
feat/newrecruit-schema-additions

Conversation

@amis92

@amis92 amis92 commented Jun 23, 2026

Copy link
Copy Markdown
Member

Summary

NewRecruit reimplemented the abandoned BattleScribe data engine and then evolved the catalogue/gameSystem data format. This models those additions as part of the format, so downstream tooling (e.g. the BattleScribe conformance spec suite) can treat the NR-superset as the base and exclude only original BattleScribe.

Model additions

  • Enum values: SelectionEntryKind +unit-group/mount/crew; ConstraintKind +exactly; ConditionKind +always/never/before; ConditionGroupKind +the numeric/comparison set (not, count, addnotEqual); ModifierKind +the math/string ops (multiplyreplace, hide).
  • New nodes: AssociationCore, AttributeTypeCore, LocalConditionGroupCore.
  • New collections: selectionEntry associations; modifier localConditionGroups; profileType attributeTypes; catalogue-root sharedForceEntries/sharedAssociations.
  • New attributes: constraint negative/automatic/message; characteristicType kind/defaultValue; profileType kind.
  • latest/Catalogue.xsd extended to declare all of the above (the single source XSD that is transformed into the three namespace schemas at build), so the model validates against the schema.

Serializer fix — preserve BattleScribe round-trip fidelity

The generated writer emits every value-type attribute (only nullable strings are skipped at runtime). So negative/automatic (non-nullable bool) would have written ="false" on every constraint, breaking round-trip with original BattleScribe data that has neither attribute (ReadWriteCatalogue/ReadWriteGamesystem).

Fix: teach WhamSerializerGenerator to honor [DefaultValue] — a value-type attribute equal to its declared default is omitted. negative/automatic carry [DefaultValue(false)], so original BattleScribe data round-trips byte-identically while NewRecruit data still emits them when set.

Tests

  • NewRecruitAdditionsTests: schema-validation of a catalogue exercising all new nodes/enums/attributes; a serialize → deserialize round-trip; and a guard that default constraint flags stay out of the output.
  • Full suite green.

🤖 Generated with Claude Code

NewRecruit evolved the BattleScribe catalogue/gameSystem data format with new
nodes, enum values, and attributes. Model them as part of the format:

- Enum values: SelectionEntryKind +unit-group/mount/crew; ConstraintKind
  +exactly; ConditionKind +always/never/before; ConditionGroupKind +the
  numeric/comparison set; ModifierKind +the math/string ops (+hide).
- New nodes: AssociationCore, AttributeTypeCore, LocalConditionGroupCore.
- New collections: selectionEntry `associations`; modifier
  `localConditionGroups`; profileType `attributeTypes`; catalogue-root
  `sharedForceEntries`/`sharedAssociations`.
- New attributes: constraint negative/automatic/message; charType
  kind/defaultValue; profileType kind.
- latest/Catalogue.xsd extended to declare all of the above so the model
  validates against the schema (Source.Tests schema-validation green).

The serializer normally writes every value-type attribute. negative/automatic
are NR-only, so emitting them on every constraint broke round-trip fidelity
with original BattleScribe data (which has neither). Teach the generated writer
to honor [DefaultValue]: a value-type attribute equal to its DefaultValue is
omitted. negative/automatic carry [DefaultValue(false)], so BattleScribe data
round-trips byte-identically while NR data still emits them when set.

Adds NewRecruitAdditionsTests: schema-validation + serialize/deserialize
round-trip for the new nodes, and a guard that default constraint flags stay
out of the output. Full wham suite green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U2KM751pP6hYS8GztAMnQw
amis92 added a commit to WarHub/battlescribe-spec that referenced this pull request Jun 23, 2026
Catalogue/gameSystem axis marked complete; roster axis audited (no confirmed
.ros format additions, one deferred question); remaining = wham re-pin after
WarHub/wham#308 merges + the optional roster association-state spike.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U2KM751pP6hYS8GztAMnQw
@amis92
amis92 merged commit 7770868 into main Jun 23, 2026
4 checks passed
@amis92
amis92 deleted the feat/newrecruit-schema-additions branch June 23, 2026 22:07
amis92 added a commit to WarHub/battlescribe-spec that referenced this pull request Jun 23, 2026
WarHub/wham#308 merged. Re-pin the submodule from the PR branch to the squash-
merge commit on wham main (7770868).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U2KM751pP6hYS8GztAMnQw
amis92 added a commit to WarHub/battlescribe-spec that referenced this pull request Jun 23, 2026
…cs (#267)

* feat(gamedata): NR-superset — discover tooling + schema-additions specs

Treat NewRecruit's additions to the BattleScribe data format as the base spec,
with original BattleScribe the excluded subset. Both NR engines must satisfy
them; battlescribe/battlescribe-ui are skipped.

- `bs-spec discover nodes|enums|xml` (DiscoverCommand): drives the frozen NR
  editor headlessly to dump creatable node types, dropdown vocabularies, and
  byte-exact serialized XML. Seed: tools/discovery/seed.yaml (outside specs/).
- Harness extended to the NR-superset: CatXmlGenerator (generic [XmlEnum]
  reflection mapper + new-node mappers), ProtocolMessages (+ association /
  attributeType / localConditionGroup), spec/protocol JSON schemas, assertion
  model (GameDataTypes/SpecModels/Runner), and both NR engines' read paths.
- 10 specs under specs/gamedata/nr/ (engines: battlescribe/battlescribe-ui
  skip), green on newrecruit + newrecruit-ui: association/exactly, selection
  entry types, type-def additions, localConditionGroup, conditionGroup types,
  extended modifier types, condition types, condition `before`, query vocab
  (associations / root-entry / limit::<costType>), shared collections.
- docs/nr-schema-additions/ inventory + docs/gamedata-coverage.md NR-superset
  section.

Re-pins .deps/wham to the NewRecruit schema-additions branch (to be re-pinned
to the merged wham SHA before this merges).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U2KM751pP6hYS8GztAMnQw

* docs(gamedata): correct roster-additions audit — no .ros format additions

Audit of the roster axis: customName/customNotes are baseline original-
BattleScribe roster format (v2_03 roster schema RosterElementBase/Roster), not
NR additions — the NR aspect is purely behavioural (NR builder can set them,
original BattleScribe can't) and is already covered by
protocol-kitchen-sink.yaml's setCustomization steps + nr-behavioral-differences.
duplicateForce and friends are runtime ops, also covered.

The one open .ros-format question — whether a roster selection serialises
chosen-association state — is deferred with reason: the army builder's roster
serializer is a separate app from the NR editor and is not reachable by the
catalogue discover trick; confirming it needs a live army-builder capture spike.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U2KM751pP6hYS8GztAMnQw

* docs(gamedata): finalize NR schema-additions README status

Catalogue/gameSystem axis marked complete; roster axis audited (no confirmed
.ros format additions, one deferred question); remaining = wham re-pin after
WarHub/wham#308 merges + the optional roster association-state spike.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U2KM751pP6hYS8GztAMnQw

* chore: re-pin .deps/wham to merged NewRecruit schema-additions SHA

WarHub/wham#308 merged. Re-pin the submodule from the PR branch to the squash-
merge commit on wham main (7770868).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U2KM751pP6hYS8GztAMnQw

* fix(cli): rename `_e` disposal local to satisfy naming analyzer

The build enforces code style as errors; `using var _e = engine;` tripped the
"prefix '_' is not expected" naming rule. Rename to `engineScope`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U2KM751pP6hYS8GztAMnQw

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
amis92 added a commit that referenced this pull request Jul 31, 2026
Restores documentation and adds the test coverage that came with #221, the
original `prepend` contribution.

`ModifierKind.Prepend` reached `main` via #308, which modelled the whole
NewRecruit block at once under a single `//` comment. The enum value and the
XSD enumeration both landed, but two things from #221 did not:

- the doc comment describing what `prepend` does, while every pre-NewRecruit
  member of this enum carries one
- test coverage — nothing anywhere in `tests/` exercised `prepend`

Adds:
- doc comments for `Prepend` (verbatim from #221) and `Replace`
- `String_modifier_kinds_round_trip` — asserts `prepend`/`append`/`replace`
  serialise to the expected wire name and survive a round-trip
- `String_modifier_kinds_are_schema_validated` — asserts they validate against
  `Catalogue.xsd`

The existing `NewRecruit_additions_round_trip` covers numeric kinds
(`multiply`) but no string operation, so this was a real gap.

Credit for the original `prepend` work goes to @The4D6 in #221.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
amis92 added a commit that referenced this pull request Jul 31, 2026
Restores documentation and adds the test coverage that came with #221, the
original `prepend` contribution.

`ModifierKind.Prepend` reached `main` via #308, which modelled the whole
NewRecruit block at once under a single `//` comment. The enum value and the
XSD enumeration both landed, but two things from #221 did not:

- the doc comment describing what `prepend` does, while every pre-NewRecruit
  member of this enum carries one
- test coverage — nothing anywhere in `tests/` exercised `prepend`

Adds:
- doc comments for `Prepend` (verbatim from #221) and `Replace`
- `String_modifier_kinds_round_trip` — asserts `prepend`/`append`/`replace`
  serialise to the expected wire name and survive a round-trip
- `String_modifier_kinds_are_schema_validated` — asserts they validate against
  `Catalogue.xsd`

The existing `NewRecruit_additions_round_trip` covers numeric kinds
(`multiply`) but no string operation, so this was a real gap.

Credit for the original `prepend` work goes to @The4D6 in #221.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
amis92 added a commit that referenced this pull request Jul 31, 2026
Restores documentation and adds the test coverage that came with #221, the
original `prepend` contribution.

`ModifierKind.Prepend` reached `main` via #308, which modelled the whole
NewRecruit block at once under a single `//` comment. The enum value and the
XSD enumeration both landed, but two things from #221 did not:

- the doc comment describing what `prepend` does, while every pre-NewRecruit
  member of this enum carries one
- test coverage — nothing anywhere in `tests/` exercised `prepend`

Adds:
- doc comments for `Prepend` (verbatim from #221) and `Replace`
- `String_modifier_kinds_round_trip` — asserts `prepend`/`append`/`replace`
  serialise to the expected wire name and survive a round-trip
- `String_modifier_kinds_are_schema_validated` — asserts they validate against
  `Catalogue.xsd`

The existing `NewRecruit_additions_round_trip` covers numeric kinds
(`multiply`) but no string operation, so this was a real gap.

Credit for the original `prepend` work goes to @The4D6 in #221.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
amis92 added a commit that referenced this pull request Jul 31, 2026
`[Unreleased]` had been empty since 0.14.0 (28 Feb) even though several
changes had landed. Brings it up to date with everything currently on `main`:

- the roster-engine stack (#315-#322) and the YAML reader (#325)
- the NewRecruit format additions (#308) and the MSBuild targets file (#313)
- the zipped-datafile fix (#324) and the dependency bump (#327)
- the `prepend` documentation and tests (#326), crediting @The4D6 for the
  original contribution in #221

The `NodeFactory.Force` breaking change is called out explicitly with the
migration, since it lands in the shipped `WarHub.ArmouryModel.Source` package.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant