What
The .NET / Sidecars coverage gate now passes, but the F# package clears it by a single line:
[sharplsp-sidecar-fsharp] coverage: 94.0034% (threshold: 95%, effective: 94% with 1pp tolerance)
That is 179 missed lines against 179 allowed. The next F# line added without a covering test turns the gate red.
Why it is fragile beyond the margin
Coverlet under-reports F#: it loses sequence points for small functions the compiler inlines, so lines that demonstrably execute are reported as never hit. Two confirmed examples, both of which run in every one of the 32 passing sidecar rename tests:
FSharpRenameToken.tokenMatchesName — its body is called on every rename via Option.filter, and reports 0 hits.
FSharpRenameIndexers.sameSymbol — IsEffectivelySameAs; a probe proved it executes (isIndexerSymbol returns true only if it does), and it reports 0 hits.
So an unknown share of the 179 "missed" lines is unreachable by any test. The gate is currently balanced on that artifact rather than on real coverage.
How it got here
FSharpRenameAliases.fs, FSharpRenameIndexers.fs and FSharpRenameToken.fs are new on the cleanup branch — roughly 560 lines of rename machinery whose only tests ran through the VS Code extension host, which the .NET coverage gate does not measure. The F# package fell to 88.77% against an unchanged 95% threshold. It was masked because _check_cov checks C# first and exits on the first failure, so neither the F# nor the Common package was evaluated while C# was red.
Coverage was restored to 94.00% by adding sidecar tests (not by lowering the threshold). This issue is about the remaining fragility.
Suggested fix
One of:
- Exclude or correct the inlined-function attribution so the number reflects reality, then set the threshold from the corrected baseline.
- Set
sharplsp-sidecar-fsharp in .config/coverage/thresholds.json to a deliberate value with the artifact documented, instead of leaving it one line from red.
Also worth changing: _check_cov should evaluate all three packages and report every failure, rather than exiting on the first. A masked gate is how this went unnoticed.
Related smaller items
src/sidecars/SharpLsp.Sidecar.FSharp/FSharpRename.fs is 516 LOC, over the 500-line limit in CLAUDE.md. Extracting the foreign-rename/transient-projection block is the natural seam but it depends on six private helpers.
FSharpRenameSemanticTests.fs / FSharpCodeActionSemanticTests.fs and HeadlessOverrideGenerationTests.cs / MergeDeclarationAssignmentTests.cs each duplicate a temp-project + code-action harness. A shared harness was started and withdrawn during a concurrent edit; it should be extracted once nothing is in flight.
What
The
.NET / Sidecarscoverage gate now passes, but the F# package clears it by a single line:That is 179 missed lines against 179 allowed. The next F# line added without a covering test turns the gate red.
Why it is fragile beyond the margin
Coverlet under-reports F#: it loses sequence points for small functions the compiler inlines, so lines that demonstrably execute are reported as never hit. Two confirmed examples, both of which run in every one of the 32 passing sidecar rename tests:
FSharpRenameToken.tokenMatchesName— its body is called on every rename viaOption.filter, and reports 0 hits.FSharpRenameIndexers.sameSymbol—IsEffectivelySameAs; a probe proved it executes (isIndexerSymbolreturns true only if it does), and it reports 0 hits.So an unknown share of the 179 "missed" lines is unreachable by any test. The gate is currently balanced on that artifact rather than on real coverage.
How it got here
FSharpRenameAliases.fs,FSharpRenameIndexers.fsandFSharpRenameToken.fsare new on thecleanupbranch — roughly 560 lines of rename machinery whose only tests ran through the VS Code extension host, which the .NET coverage gate does not measure. The F# package fell to 88.77% against an unchanged 95% threshold. It was masked because_check_covchecks C# first and exits on the first failure, so neither the F# nor the Common package was evaluated while C# was red.Coverage was restored to 94.00% by adding sidecar tests (not by lowering the threshold). This issue is about the remaining fragility.
Suggested fix
One of:
sharplsp-sidecar-fsharpin.config/coverage/thresholds.jsonto a deliberate value with the artifact documented, instead of leaving it one line from red.Also worth changing:
_check_covshould evaluate all three packages and report every failure, rather than exiting on the first. A masked gate is how this went unnoticed.Related smaller items
src/sidecars/SharpLsp.Sidecar.FSharp/FSharpRename.fsis 516 LOC, over the 500-line limit in CLAUDE.md. Extracting the foreign-rename/transient-projection block is the natural seam but it depends on six private helpers.FSharpRenameSemanticTests.fs/FSharpCodeActionSemanticTests.fsandHeadlessOverrideGenerationTests.cs/MergeDeclarationAssignmentTests.cseach duplicate a temp-project + code-action harness. A shared harness was started and withdrawn during a concurrent edit; it should be extracted once nothing is in flight.