Fix function-level merge gap handling (compile-breaking defects 1+2) - #27
Merged
Merged
Conversation
…g bug) Fixes both compile-breaking defects in docs/bugs/ function-level-merge-gap-handling.md: - Plain member declarations (var/default/autobind) are extracted as scope-qualified units (ScriptUnit.ScopedName, via a top-level type-range prescan) so a mod adding one participates in per-unit resolution instead of being silently dropped with vanilla's gap text (defect 2). - Insertion slots emit the inserting side's own contiguous span, preserving position relative to class braces and the separators between consecutive inserted units (defect 1); ambiguous placements (both sides inserting at a brace-carrying slot, or deleted anchors next to one) decline instead of guessing. - A post-reassembly sanity gate declines any rescue whose output has a member-shaped declaration at brace depth 0 or unbalanced braces - validated against the real broken output (fails on exactly the orphaned accessor the game rejected) with zero false positives on real vanilla r4Player.ws/player.ws/actor.ws/baseEffect.ws, all of which also round-trip byte-exact through the extended extractor. 17 new tests (145 total). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GXAuGMLB44T5Zv5o5ZzKah
This was referenced Aug 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes both compile-breaking defects documented in
docs/bugs/function-level-merge-gap-handling.md(committed here alongside the fix), found on a real 198-mod install where the function-level rescue produced merged output forr4Player.ws/player.ws/damageManagerProcessor.wsthat the game refused to compile.Defect 2 (dropped declarations) — plain member declarations (
private var x : bool;,default y = 4.5f;) lived in "gap" territory, which always reverted to vanilla's text on reassembly, silently dropping mod-added declarations while the code referencing them survived. Fix:var/default/autobindstatements now extract asScriptUnitKind.MemberDeclarationunits and participate in per-unit resolution. Unit identity is scope-qualified (ScriptUnit.ScopedName, e.g.CR4Player::mCSMCR; states asCombat@CR4Player::phase) via a top-level type-range prescan — member names recur across the several classes a real.wsfile contains, so bare-name alignment would mis-align them.UnitAlignernow matches onScopedName.Defect 1 (units emitted outside their class, separators eaten) — vanilla's gap at an insertion slot can contain the class-closing brace, and the old emission appended inserted units after the whole vanilla gap (
'public' has no sense for global function ...), with the separators that live in the side's own gaps discarded (}<TAB>public function ...). Fix: a slot where exactly one side inserts emits that side's own contiguous span (its gaps + units, verbatim), preserving both brace-relative position and separators. Ambiguous placements — both sides inserting at a brace-carrying slot, or a deleted anchor beside one — decline to the conflict-marker sidecar instead of guessing.Post-reassembly sanity gate (bug doc's suggested check 1) —
PassesReassemblySanityGate(public, so validation tooling can reuse it) walks the output's structural mask and declines any rescue whose output has a member-shaped declaration at brace depth 0, negative/nonzero final brace depth, or content that no longer scans. This catches whole classes of future interleaving bugs, not just the two known ones.Real-data validation
public function GetVoiceSetLastPlayed() : floatat global scope), and passes the hand-repaired live merged files and vanillar4Player.ws— zero false positives.r4Player.ws(1075 units, 308 of them member declarations),player.ws,damageManagerProcessor.ws,actor.ws,baseEffect.ws; worst-case extract 118 ms.overwritesupport PR, next).Tests
17 new tests (145 total, all green): defect-shaped fixtures (member appended at class end + following global exec function; declaration added while the other side edits a function; default-value change; both-sides-insert separation + decline-at-class-boundary), scoped extraction (multi-declarator lists,
default:xidentity, same member name across two classes, state-in-parent scoping, struct members, locals-never-extracted, @addField precedence), and the gate directly (orphaned member, mangled-onto-brace-line, valid global shapes, unbalanced braces).One test in this suite caught a real bug in the fix itself during development (a vanilla unit following synthesized insertions lost its separator) — fixed via
endedOnSynthesizedUnitthreading.dotnet build/dotnet test(145/145) /dotnet format whitespace --verify-no-changesall green.🤖 Generated with Claude Code
https://claude.ai/code/session_01GXAuGMLB44T5Zv5o5ZzKah