Repository navigation
Fix narrowing-rule unapplication across an interposed optional node - #540
Open
johnml1135 wants to merge 2 commits into
Open
johnml1135 wants to merge 2 commits into
johnml1135 wants to merge 2 commits into
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #540 +/- ##
==========================================
+ Coverage 74.38% 74.41% +0.03%
==========================================
Files 456 456
Lines 38343 38355 +12
Branches 5264 5268 +4
==========================================
+ Hits 28522 28543 +21
+ Misses 8665 8656 -9
Partials 1156 1156 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This comment has been minimized.
This comment has been minimized.
This was referenced Oct 10, 2026
johnml1135
force-pushed
the
fix/narrow-interposed-optional
branch
from
October 15, 2026 01:31
ade7402 to
fef6889
Compare
johnml1135
changed the base branch from
master
to
integrate-conformance-framework
October 15, 2026 01:32
This comment has been minimized.
This comment has been minimized.
johnml1135
force-pushed
the
integrate-conformance-framework
branch
from
October 15, 2026 21:21
3a63132 to
df2cf21
Compare
johnml1135
force-pushed
the
fix/narrow-interposed-optional
branch
from
October 16, 2026 13:19
fef6889 to
4ed11ad
Compare
johnml1135
changed the base branch from
integrate-conformance-framework
to
master
October 16, 2026 13:19
This comment has been minimized.
This comment has been minimized.
NarrowAnalysisRewriteRuleSpec.Unapply located the matched replacement nodes by walking Next from the match start, both when unioning the left-hand side into them and when marking the surplus ones optional. The analysis matcher skips optional nodes and filters out boundaries, so a multi-node replacement match can span one; the walk then landed on that node instead of the real replacement node, which stayed mandatory, and the underlying reading of the root was unreachable. Read each replacement position back from its own group capture in the analysis pattern, as FeatureAnalysisRewriteRuleSpec does, in both loops, keeping the union encoding and its variable guards. A position whose group captured nothing (a quantifier in the replacement) ends the union and is not marked. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
johnml1135
force-pushed
the
fix/narrow-interposed-optional
branch
from
October 20, 2026 17:27
4ed11ad to
bd15f64
Compare
This comment has been minimized.
This comment has been minimized.
…l segment Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Public API, target framework, package, machine.py parity: None verified. Findings: F1 addressed; F2 addressed; F3 addressed; F4 withdrawn; F5 addressed. Reviewed at 3e99f8e |
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.
Quick summary
A root is no longer lost in analysis when a deletion rule ordered after a narrowing rule reinserts an optional node inside the narrowing rule's replacement. Only parses that were silently dropped change; the rest of the HermitCrab suite is unchanged. Synthesis, feature rules, and #539's union behaviour are untouched.
Where to look
FeatureAnalysisRewriteRuleSpecalready does --RewriteRuleTests.MergeRuleUnappliesAcrossInterposedOptionalSegment(red on master, line 636 assertion; green here).RewriteRuleTests.MergeRuleUnappliesAcrossInterposedOptionalBoundary(red on master, green here).RewriteRuleTests.RightToLeftMergeRuleUnappliesAcrossInterposedOptionalSegment(red on master, green here).u -> i i) share this spec and this walk --RewriteRuleTests.ExpandRuleUnappliesAcrossInterposedOptionalSegment(root lost on master, green here);RewriteRuleTests.ExpandRulesstill passes.RewriteRuleTests.MergeRuleWithOptionalReplacementSegmentSkipsEmptyCapture(NullReferenceException without theSuccessguard, green here; it also passes on master, whose raw walk happens to land on real nodes for this word).Deliberately not included
NarrowSynthesisRewriteSubruleSpec.ApplyRhs) that such a rule still trips when it fires. Both are pre-existing and separate from this walk.Validation (head 3e99f8e)
dotnet test tests/SIL.Machine.Morphology.HermitCrab.Tests -c Release -m:4 -nodeReuse:falsewith master'sNarrowAnalysisRewriteRuleSpec.cs(which includes Fix a performance bug in NarrowAnalysisRewriteRuleSpec #539) -- the interposed-node tests fail, including the right-to-left and expansion cases (the expansion test returns no analyses).dotnet test tests/SIL.Machine.Tests -c Release -m:4 -nodeReuse:false-- Passed: 887, Skipped: 3, Failed: 0.pwsh ./scripts/comment-hygiene.ps1-- clean.dotnet csharpier check .-- Checked 720 files, clean.git diff --check origin/master...HEAD-- clean../local_check.sh --agent-strictas a whole.Not CI-equivalent: no coverage collected.
Reading this a year from now
NarrowAnalysisRewriteRuleSpec.Unapplyhandles a rulel1..ln -> r1..rmin analysis by insertingl1..lnas optional nodes after the match and marking the matchedr1..rmoptional, so lexical lookup can read either the surface form or the underlying one. It foundr1..rmby walkingrange.Start.Nextm times. The analysis matcher skips optional nodes and never sees boundaries (its filter is Segment|Anchor), so a match ofr1 r2can span an interior optional node or a boundary. The raw walk then marked that node instead ofr2;r2stayed mandatory and thel1..lnreading was unreachable.Reproduction (test fixture language,
Morphophonemicstratum): synthesis orders a g -> n i, thenz -> 0 / n _ i, which is vacuous in synthesis. In analysis the deletion unapplies first and inserts(z)betweennandi; the merge then matchesn (z) iand the walk marksnand(z), leavingimandatory. Surfacenishould parse to root 32 (sag) and did not. Replacingzwith the boundary+reproduces the same loss through the matcher's filter instead of through an optional node.The fix wraps each replacement constraint in a named group in the analysis pattern and marks the captured node of each group optional. That is the encoding
FeatureAnalysisRewriteRuleSpecalready uses for its per-position read-back.Paths not taken
range.Start.Nextwhile skipping optional and boundary nodes. Rejected: it re-derives what the matcher already decided, and would disagree with the matcher whenever its skip rules change.🤖 Generated with Claude Code
This change is