Repository navigation
feat(settings): carry blocks under catalogued keys as written - #413
Conversation
A braced block under a catalogued non-list settings key carries an undeclared value the same way an uncatalogued-key block does: the parser produces an opaque Group under the canonical key, the emitter writes it under the localized display name, and check accepts it with opaque leaf checking. The pinned source compilers emit this shape (opy-rs#496), so emission gains nothing the parser cannot read back. Fixes #412
e54-bot
left a comment
There was a problem hiding this comment.
Reviewed the diff against issue #412 and verified end-to-end via the CLI in this worktree.
Verified correct
- Parse:
Map Rotation { a }underlobbyproducesGroup{mapRotation}with opaque children; canonical name comes fromentry.path.last(), cursor past{is correct. Verified for Enum (lobby.mapRotation), Flag (extensions.beamEffects), exact-path mode key (gamemodes.assault.roleLimit), and hero-context key (heroes.<team>.<hero>.health%under Mei emits the hero label). - Emit/check parity at every member position:
walk_*dispatch mirrorsemit_*;(Group, _)carried with opaque leaf checking at empty path on both sides. uncatalogued_membersreports the group once →ProjectDefinedConstruct, no suggestion — correct since the key itself is declared.- Localization: zh-CN emits
地图轮换 { a b };member_display_nameis used identically to typed members. - Round-trip is a fixed point (emitted output reparses and re-emits identically — verified via
emit | emit). - Scalar-kind mismatch rejection unchanged (
member::acceptuntouched; covered by existing tests). - Boundary check: under
heroes.<team>, a catalogued-key block parses to aGroupbut check/emit reject it asunknown hero 'health%'— identical to uncatalogued groups at that path (positional hero-group dispatch predates this PR), so contract is internally consistent there.
Findings
-
(minor, docs)
docs/language-support/settings.md"Members outside the catalog" (~L131-148) enumerates the carried shapes — unknown-key leaf →Raw, unknown-key block →Group, catalogued key + undeclared scalar →RawValue— but omits the new shape: a braced block under a catalogued non-list key now parses toGroupunder the canonical key and emits via the localized display name. The section currently implies only unknown keys produceGroups. Repo AGENTS.md requires the owning durable doc to update in the same PR for supported-behavior changes. (Related:check.rsmodule doc L5-8 says "Members the catalog does not declare are carried as written" — now also catalogued-key blocks.) -
(minor, test)
issue_412_catalogued_key_block_round_trips_as_written(tests/settings_pipeline.rs:1606) is named for round-trip and the issue acceptance explicitly requires "emittedMap Rotation { a }text parses, validates, and re-emits identically", but the test never reparsesemitted—assert_check_compile_parity(source)reparses the source. Addparser::parse(&emitted)+ validate/re-emit equality (or equivalent) to cover the criterion. -
(minor)
parser.rs:276-279— the_ => "only settings lists may use braces"arm inside the list-element match is now unreachable: the new early return guaranteesentry.kind ∈ {ListMap, ListHero}there. Harmless (a catch-all is needed for exhaustiveness), but the dead error may mislead; considerunreachable!or restructuring so the code reflects that only list kinds reach this loop.
Verdict: findings
e54-bot
left a comment
There was a problem hiding this comment.
Re-review of bd0633b against the three prior findings.
Prior findings — all resolved
- Docs:
docs/language-support/settings.md:141-143adds the catalogued-non-list-key block shape (SettingsNode::Groupunder the canonical key, emitted via localized display name, contents as written) — verified accurate against parser.rs:236-238 and emitter.rs:237-242.check.rsmodule doc L5-7 now names both block positions. Documentation impact satisfied in-PR per repo AGENTS.md. - Test:
issue_412_catalogued_key_block_round_trips_as_writtennow reparsesemitted, validates, and asserts re-emit equality — the literal acceptance criterion. The chain also discriminates correctly:mapRotationisKeyKind::Enum, so a mis-parse toListwould failvalidate(). parser.rs:276unreachable!is sound:entryis an immutable&TableEntrybound before the LBrace branch, and the early return at :236-239 guaranteesentry.kindisListMap/ListHeroat the elements loop. No input can reach it.
New-diff check (bd0633b): no regressions — doc text matches behavior, reparse assertions exercise the criterion, removed error string has no remaining references. Independently re-verified: check/emit (Group, _) symmetry with opaque leaf checking at empty path, carried_suggestion returning None for (Group, Some) (declared key, not a misspelling), and team-level hero-group dispatch identical for catalogued and uncatalogued blocks (pre-existing positional behavior).
Verification: cargo test -p workshop-rs — 281 lib + 166 integration tests, 0 failures; fmt/clippy/git diff --check clean.
LGTM — no findings.
Summary
Carries a braced settings block under a catalogued non-list key as an opaque block, closing the canonical gap reported in #412:
Map Rotation { a }underlobbynow parses to aSettingsNode::Groupunder the canonicalmapRotationkey instead of failing withonly settings lists may use braces.ListMap/ListHerokeep their typed-list meaning.Groupat a catalogued path emits<localized display name> { <opaque children> }— same block shape as uncatalogued keys, resolved name.check_emission/validateaccept the group; its leaves are checked as opaque members, and it is reported throughuncatalogued_members(aProjectDefinedConstructresidual, no suggestion).Motivation: the pinned OverPy oracle writes
Map Rotation { a }for"lobby": {"mapRotation": ["a"]}(opy-rs#496) — an undeclared value under a translated catalogued key. Without this, neither opy-rs emission nor raw parse-back can represent the shape.Verification
cargo fmt --all --check,cargo clippy --workspace --all-targets -- -D warnings— cleancargo test --workspace --all-targets— all pass (281 lib + 166 integration + 30 cli)cargo run -p workshop-rs --bin workshop-catalog-gen -- check— cleanissue_412_catalogued_key_block_round_trips_as_written(parse → validate → emit → re-parse parity, residual classification) andissue_412_catalogued_key_block_emits_the_display_name(programmatic tree, zh-CN localization)Numberunder aFlagkey) still reject, covered by existing tests