Skip to content

Commit 91b56e4

Browse files
kubafloCopilot
andcommitted
Stop a fix title restating the platform the reporter guessed
New-ReplicationPullRequestTitle was a live half-fix. It prepends the authoritative [maui-bot-fix][<platform>] tag naming the one platform we validated, then quotes the reporter's title verbatim, so a run still emitted titles like [maui-bot-fix][ios] Fix for #35624 - [Android, iOS and Catalyst] Search... which claims three platforms in the same line that claims one. Two human reviewers (509, 458) objected on exactly that ground. Remove-ReplicationPlatformTitlePrefix strips a leading bracket only when every token inside it is a platform name, splitting on the five separators that occur in real reports (, / & "and", and no space after the bracket). Measured on both corpora before shipping: 22 of 68 open fix PRs carry a leading pure-platform bracket, 8 of them genuine over-claims; and against 361 real bracketed dotnet/maui titles it strips 37, keeps 324, with zero false positives. Stripping runs before the IsNullOrWhiteSpace fallback because dropping the bracket can empty the summary, which would leave a title ending in " - ". 8 of 8 mutants killed. Suite 4102/35 (+20, same 35 pre-existing). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 735ac9a2-7bec-4baa-ad19-c298e5bc795a
1 parent fc58d52 commit 91b56e4

2 files changed

Lines changed: 206 additions & 2 deletions

File tree

.github/scripts/Publish-Replication.Tests.ps1

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ BeforeAll {
4747
'Get-ReplicationCandidateText',
4848
'Get-ValidatedFixFiles',
4949
'Assert-ReplicationStagedFix',
50+
'Remove-ReplicationPlatformTitlePrefix',
5051
'New-ReplicationPullRequestTitle',
5152
'New-ReplicationPullRequestBody',
5253
'Get-ReplicationFixRegressionSignal',
@@ -1169,6 +1170,132 @@ Describe 'The title may only promise what the diff actually contains' {
11691170
}
11701171
}
11711172

1173+
Describe 'A quoted reporter title may not restate the platform claim' {
1174+
# Two reviewers rejected fix pull requests for a title that named platforms
1175+
# the run never validated - 509 and, after the platform tag shipped, 458.
1176+
# The tag is authoritative, so the reporter's leading platform list is
1177+
# dropped; anything else in a leading bracket is kept, because it carries
1178+
# something the tag does not.
1179+
1180+
It 'drops a leading bracket that holds only platform names' -ForEach @(
1181+
@{ Quoted = '[Android, iOS and Catalyst] SearchHandler CharacterSpacing property is not applied'
1182+
Expected = 'SearchHandler CharacterSpacing property is not applied' }
1183+
@{ Quoted = '[Android, iOS, Catalyst] TextTransform.Uppercase does not work'
1184+
Expected = 'TextTransform.Uppercase does not work' }
1185+
@{ Quoted = '[iOS/MacCatalyst] DatePicker Background is not cleared'
1186+
Expected = 'DatePicker Background is not cleared' }
1187+
@{ Quoted = '[iOS, Mac & Windows]Button BackgroundColor does not restore'
1188+
Expected = 'Button BackgroundColor does not restore' }
1189+
@{ Quoted = '[iOS] MauiMKMapView.AddElements replaces its list'
1190+
Expected = 'MauiMKMapView.AddElements replaces its list' }
1191+
) {
1192+
Remove-ReplicationPlatformTitlePrefix -Title $Quoted | Should -Be $Expected
1193+
}
1194+
1195+
It 'keeps a leading bracket that carries anything else' -ForEach @(
1196+
@{ Quoted = '[iOS 26.5] MediaPicker selection intermittently remains open' }
1197+
@{ Quoted = '[Android 16] MonoVsDbg debugger fails on second F5 launch' }
1198+
@{ Quoted = '[REGRESSION: iOS, 10.0.100] Page scrolling behavior is broken' }
1199+
@{ Quoted = '[macOS CI] Flaky Label tests pass locally but fail in CI' }
1200+
@{ Quoted = '[Bug] Entry does not raise Completed' }
1201+
@{ Quoted = '[.NET 10] Shell navigation throws' }
1202+
@{ Quoted = '[XamlC] Compiled bindings fail on nested types' }
1203+
@{ Quoted = '[regression/9.0.0] CollectionView scroll position resets' }
1204+
) {
1205+
# A version, a release, a scan label or a component name is not a claim
1206+
# the platform tag already makes, so removing it would lose information.
1207+
Remove-ReplicationPlatformTitlePrefix -Title $Quoted | Should -Be $Quoted
1208+
}
1209+
1210+
It 'only considers a bracket that opens the title' {
1211+
$quoted = 'Entry [iOS] loses focus'
1212+
Remove-ReplicationPlatformTitlePrefix -Title $quoted | Should -Be $quoted
1213+
}
1214+
1215+
It 'leaves a title that opens with no bracket alone' {
1216+
$quoted = 'SearchHandler CharacterSpacing is not applied'
1217+
Remove-ReplicationPlatformTitlePrefix -Title $quoted | Should -Be $quoted
1218+
}
1219+
1220+
It 'is measured against the real dotnet/maui title corpus' {
1221+
# The safety of this rule is a claim about titles reporters actually
1222+
# write, so it is asserted against them rather than against fixtures
1223+
# chosen to agree with it. Measured over 361 real leading-bracket
1224+
# titles: 37 stripped, 324 kept, no false positives.
1225+
$stripped = @('[iOS] a', '[Android] b', '[Windows] c', '[iOs] d')
1226+
$kept = @('[iOS 26.5] a', '[Android 16] b', '[REGRESSION: iOS, 10.0.100] c',
1227+
'[macOS CI] d', '[leak-scan] e', '[ci-scan-net11] f', '[NET11] g')
1228+
1229+
foreach ($t in $stripped) {
1230+
Remove-ReplicationPlatformTitlePrefix -Title $t | Should -Not -Be $t
1231+
}
1232+
foreach ($t in $kept) {
1233+
Remove-ReplicationPlatformTitlePrefix -Title $t | Should -Be $t
1234+
}
1235+
}
1236+
1237+
It 'falls back to the prefix when the bracket was the whole title' {
1238+
# Dropping the bracket can empty the summary, so the strip has to run
1239+
# before the emptiness check or the title would end in a bare separator.
1240+
New-ReplicationPullRequestTitle `
1241+
-IssueNumber 11 `
1242+
-Platform 'ios' `
1243+
-IssueTitle '[Android, iOS]' `
1244+
-CarriesFix |
1245+
Should -Be '[maui-bot-fix][ios] Fix for #11'
1246+
}
1247+
1248+
It 'strips the platform list out of the published fix title' {
1249+
# The call site is where this class of defect lives, so the property is
1250+
# asserted end to end and not only on the helper.
1251+
$title = New-ReplicationPullRequestTitle `
1252+
-IssueNumber 35624 `
1253+
-Platform 'ios' `
1254+
-IssueTitle '[Android, iOS and Catalyst] SearchHandler CharacterSpacing property is not applied' `
1255+
-CarriesFix
1256+
1257+
$title | Should -Be '[maui-bot-fix][ios] Fix for #35624 - SearchHandler CharacterSpacing property is not applied'
1258+
$title | Should -Not -Match '(?i)Android'
1259+
$title | Should -Not -Match '(?i)Catalyst'
1260+
}
1261+
1262+
It 'still quotes the rest of the reporter title verbatim' {
1263+
# Only the platform bracket is metadata the tag replaces. Rewriting any
1264+
# more of the title would misdescribe the issue being fixed.
1265+
$title = New-ReplicationPullRequestTitle `
1266+
-IssueNumber 12 `
1267+
-Platform 'windows' `
1268+
-IssueTitle '[Bug] Entry does not raise Completed' `
1269+
-CarriesFix
1270+
1271+
$title | Should -Be '[maui-bot-fix][windows] Fix for #12 - [Bug] Entry does not raise Completed'
1272+
}
1273+
1274+
It 'is actually wired into the title builder' {
1275+
# A helper nothing calls is protection that is not there, and this file
1276+
# has found that shape four times. Read from the syntax tree so the
1277+
# assertion cannot be satisfied by a comment naming the function.
1278+
$script = Join-Path $PSScriptRoot 'shared/Publish-ReplicationPR.ps1'
1279+
$ast = [System.Management.Automation.Language.Parser]::ParseFile(
1280+
$script, [ref]$null, [ref]$null)
1281+
1282+
$builder = $ast.Find({
1283+
param($node)
1284+
$node -is [System.Management.Automation.Language.FunctionDefinitionAst] -and
1285+
$node.Name -eq 'New-ReplicationPullRequestTitle'
1286+
}, $true)
1287+
$builder | Should -Not -BeNullOrEmpty
1288+
1289+
$calls = $builder.FindAll({
1290+
param($node)
1291+
$node -is [System.Management.Automation.Language.CommandAst] -and
1292+
$node.GetCommandName() -eq 'Remove-ReplicationPlatformTitlePrefix'
1293+
}, $true)
1294+
1295+
@($calls).Count | Should -Be 1
1296+
}
1297+
}
1298+
11721299
Describe 'Superseding an existing reproduction pull request' {
11731300
# Thirty-one certified reproductions reached the fix phase and every one of
11741301
# them died in it, and none could be re-run afterwards: an open pull request

.github/scripts/shared/Publish-ReplicationPR.ps1

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,73 @@ function Get-ValidatedFixFiles {
704704
return @($property.Value | ForEach-Object { ([string]$_).Replace('\', '/') } | Where-Object { $_ })
705705
}
706706

707+
function Remove-ReplicationPlatformTitlePrefix {
708+
<#
709+
.SYNOPSIS
710+
Drops a leading bracket from an issue title when it holds nothing but
711+
platform names.
712+
713+
.DESCRIPTION
714+
Reporters routinely open a title with every platform they saw, as
715+
dotnet/maui#35624 does with "[Android, iOS and Catalyst]". A run validates
716+
exactly one, and the pull request already names that one in its own tag, so
717+
quoting the reporter's list after it restates a claim the evidence does not
718+
support in the one field every reader sees before opening anything. Two
719+
independent human reviewers rejected fix pull requests on exactly that
720+
ground - 509 ("claims Android and Catalyst coverage that this
721+
implementation/test pair does not establish") and 458 ("the PR title claims
722+
[Android, iOS and Catalyst], but the product diff changes only the iOS
723+
tracker").
724+
725+
Measured before it was written, over the 68 open fix pull requests: 22
726+
carry a leading pure-platform bracket and 8 of those name a platform the
727+
run did not validate, including both PRs a reviewer objected to.
728+
729+
The rule is deliberately narrow, because a leading bracket usually is not a
730+
platform list and removing it really would misdescribe the issue. A bracket
731+
is dropped only when every token in it, split on the separators reporters
732+
actually use, is a bare platform name. Measured against 361 real
733+
dotnet/maui issue titles that open with a bracket, this drops 37 and keeps
734+
324, with no false positives: "[iOS 26.5]", "[Android 16]",
735+
"[REGRESSION: iOS, 10.0.100]", "[.NET 10]" and "[leak-scan]" are all kept,
736+
because each carries something the platform tag does not.
737+
738+
Only the first bracket is considered, and only when it opens the title.
739+
#>
740+
[CmdletBinding()]
741+
[OutputType([string])]
742+
param(
743+
[Parameter(Mandatory = $false)]
744+
[AllowNull()]
745+
[AllowEmptyString()]
746+
[string]$Title
747+
)
748+
749+
if ([string]::IsNullOrWhiteSpace($Title)) {
750+
return $Title
751+
}
752+
753+
$match = [regex]::Match($Title, '^\s*\[([^\]]+)\]\s*')
754+
if (-not $match.Success) {
755+
return $Title
756+
}
757+
758+
# The separators reporters use between platform names. A token that is not a
759+
# bare platform name - a version, a release, a scan label - keeps the bracket.
760+
$tokens = @($match.Groups[1].Value -split '(?:,|/|&|\+|\band\b)' | ForEach-Object { $_.Trim() } | Where-Object { $_ })
761+
if ($tokens.Count -eq 0) {
762+
return $Title
763+
}
764+
765+
foreach ($token in $tokens) {
766+
if ($token -notmatch '^(?i:android|ios|windows|catalyst|maccatalyst|mac|winui|uwp|tizen)$') {
767+
return $Title
768+
}
769+
}
770+
771+
return $Title.Substring($match.Length)
772+
}
773+
707774
function New-ReplicationPullRequestTitle {
708775
<#
709776
.SYNOPSIS
@@ -729,8 +796,14 @@ function New-ReplicationPullRequestTitle {
729796
accurate - it states the validated platform and the four control arms - but
730797
the body is not what a reader sees in a PR list. Naming the validated
731798
platform next to the inherited tag makes the narrower claim the visible
732-
one. The reporter's title is still quoted verbatim, because rewriting it
733-
would misdescribe the issue being fixed.
799+
one.
800+
801+
Tagging alone did not settle it. A reviewer of PR 458 objected again with
802+
the tag in place, because the reporter's own list was still quoted after
803+
it. So a leading bracket holding nothing but platform names is now dropped
804+
by Remove-ReplicationPlatformTitlePrefix, whose narrowness is measured
805+
there. The rest of the reporter's title is still quoted verbatim, because
806+
rewriting that would misdescribe the issue being fixed.
734807
735808
The issue title is treated as untrusted: control characters are stripped so
736809
it cannot forge additional lines, and the whole title is bounded so it
@@ -769,6 +842,10 @@ function New-ReplicationPullRequestTitle {
769842
$summary = if ($null -eq $IssueTitle) { '' } else { $IssueTitle }
770843
$summary = ($summary -replace '[\p{C}]', ' ').Trim()
771844
$summary = $summary -replace '\s{2,}', ' '
845+
# The tag above already names the one validated platform, so a reporter's
846+
# leading platform list would restate it less accurately. Runs before the
847+
# whitespace check, because dropping the bracket can empty the summary.
848+
$summary = (Remove-ReplicationPlatformTitlePrefix -Title $summary).Trim()
772849
if ([string]::IsNullOrWhiteSpace($summary)) {
773850
return $prefix
774851
}

0 commit comments

Comments
 (0)