Release v1.27.0 - #571
Merged
Merged
Release v1.27.0#571
Conversation
Captured from the 1.26.0 UI: the plan view with the rebuilt insights strip and icon set, and Advice for Humans as severity cards. Worth saying plainly: the screenshots the README already points at are now stale. They show emoji toolbar icons, the old monospace advice dump and the pre-overhaul properties panel, none of which the app looks like any more. These two are the current article; swapping the README over to them is a separate pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Add 1.26 screenshots
Every screenshot the README pointed at showed the pre-1.26 UI: emoji toolbar icons, the monospace advice dump, the properties panel before the per-thread rows were folded up, and a plan comparison that was a wall of text. Someone landing on the repo saw a product that no longer exists. All seven recaptured from the shipped UI. Three captions were describing the old behaviour and are rewritten rather than left to contradict the picture above them: advice is cards now and not a "one-click text report", comparison is a metric diff and not "side-by-side", and the properties panel is worth describing in terms of what it stopped doing. The two 1.26-suffixed files added for the release announcement are folded into the canonical names here, so there is one copy of each. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…-1.26 Refresh the README screenshots for 1.26
ci.yml skipped itself on docs-only changes via paths-ignore. That is fine while nothing depends on it and fatal the moment the check is required: a workflow skipped by a path filter reports nothing at all, GitHub leaves the check pending forever, and the PR can never merge. The screenshots PR that just went in would have been unmergeable. The filtering moves into the job. It always runs, so the check always reports; the steps that cost anything are guarded and skip on a docs-only change. This is the shape PerformanceMonitor's build.yml already uses, and its comment says why. The filter is a positive list of code paths rather than the old ignore list inverted. paths-filter ORs the patterns within a filter, so a stack of negated patterns matches whenever a file fails any one of them, which for a docs-only change is always true. Listing what IS code cannot go wrong that way. PlanViewer.Ssms and PlanViewer.Ssms.Installer stay out because they are not in the solution and ci.yml never built them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…heck Make the CI check report on every PR
Bumps [signpath/github-action-submit-signing-request](https://github.com/signpath/github-action-submit-signing-request) from 2 to 3. - [Release notes](https://github.com/signpath/github-action-submit-signing-request/releases) - [Commits](SignPath/github-action-submit-signing-request@v2...v3) --- updated-dependencies: - dependency-name: signpath/github-action-submit-signing-request dependency-version: '3' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps Avalonia from 11.3.20 to 11.3.22 Bumps Avalonia.Desktop from 11.3.20 to 11.3.22 Bumps Avalonia.Fonts.Inter from 11.3.20 to 11.3.22 Bumps Avalonia.Headless from 11.3.20 to 11.3.22 Bumps Avalonia.Themes.Fluent from 11.3.20 to 11.3.22 Bumps Microsoft.Data.SqlClient from 7.0.3 to 7.1.0 Bumps Microsoft.Data.SqlClient.Extensions.Azure from 7.0.3 to 7.1.0 Bumps Microsoft.NET.Test.Sdk from 18.10.0 to 18.10.1 Bumps Microsoft.Testing.Extensions.HangDump from 2.4.0 to 2.4.1 --- updated-dependencies: - dependency-name: Avalonia dependency-version: 11.3.22 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: patch-and-minor - dependency-name: Avalonia.Desktop dependency-version: 11.3.22 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: patch-and-minor - dependency-name: Avalonia.Fonts.Inter dependency-version: 11.3.22 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: patch-and-minor - dependency-name: Avalonia.Themes.Fluent dependency-version: 11.3.22 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: patch-and-minor - dependency-name: Microsoft.Data.SqlClient dependency-version: 7.1.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: patch-and-minor - dependency-name: Microsoft.Data.SqlClient.Extensions.Azure dependency-version: 7.1.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: patch-and-minor - dependency-name: Microsoft.Data.SqlClient dependency-version: 7.1.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: patch-and-minor - dependency-name: Microsoft.Data.SqlClient.Extensions.Azure dependency-version: 7.1.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: patch-and-minor - dependency-name: Microsoft.Data.SqlClient dependency-version: 7.1.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: patch-and-minor - dependency-name: Microsoft.Data.SqlClient.Extensions.Azure dependency-version: 7.1.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: patch-and-minor - dependency-name: Avalonia dependency-version: 11.3.22 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: patch-and-minor - dependency-name: Avalonia.Desktop dependency-version: 11.3.22 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: patch-and-minor - dependency-name: Avalonia.Fonts.Inter dependency-version: 11.3.22 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: patch-and-minor - dependency-name: Avalonia.Headless dependency-version: 11.3.22 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: patch-and-minor - dependency-name: Avalonia.Themes.Fluent dependency-version: 11.3.22 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: patch-and-minor - dependency-name: Microsoft.Data.SqlClient dependency-version: 7.1.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: patch-and-minor - dependency-name: Microsoft.Data.SqlClient.Extensions.Azure dependency-version: 7.1.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: patch-and-minor - dependency-name: Microsoft.NET.Test.Sdk dependency-version: 18.10.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: patch-and-minor - dependency-name: Microsoft.Testing.Extensions.HangDump dependency-version: 2.4.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: patch-and-minor ... Signed-off-by: dependabot[bot] <support@github.com>
Connecting to a server from a fresh Query tab changed neither of the two things the "Get started" overlay watched (typed text, open documents), so the opaque panel kept covering the editor and kept offering "Connect to a server" on a session that just did. The editor was alive and focused underneath, but nothing said so. A connected session is in use even with nothing typed: the empty-state predicate now also requires having no server connection, and the connect block refreshes it right after flipping the toolbar, before the metadata round trips, so the editor appears the moment the dialog closes. The connection dialog is the only place a session ever gains a connection, and nothing ever takes one away, so that one call site covers every path. TheOverviewDoesNotCountAsHavingOpenedSomething pinned the old behavior (overlay up on a connected session, a state the app can no longer reach); its real claim, that the Overview opens no documents, was already carried by its strip assertion, so its overlay pin now points down. Fixes #540 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sr43FouJ6RizUyoWQeR5FX
…nect Show the editor as soon as a session connects
The connection dialog only closes with true after it opened a real connection and enumerated the databases the login can see, through the database the user named — the one some logins (Azure SQL DB, JIT access) can open when master is off limits. Both connect handlers then flipped their toolbar green and re-enumerated through a second, hardcoded-master connection, swallowing failure: a server blip in that window, or a master-blocked login every time, left a green "connected" toolbar over a disabled, empty database picker. The dialog now hands its list over (ResultDatabases) and the session and plan toolbars fill their pickers from it; both redundant enumerations are deleted, so the green flip is always backed by the connection that just succeeded, and connecting costs one round trip fewer. The plan-side enumeration's READ UNCOMMITTED hint moves to the dialog's query, which replaced it. The standalone actual-plan tab told the same lie by a different route: SetConnectionStatus painted the label green while the picker stayed at its XAML-disabled default, and _planConnection was never set, so a filled picker could not have switched the connection string anyway. AdoptConnection takes the validated connection plus the dialog's list and wires all of it. Follow-up to #540 / #541. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sr43FouJ6RizUyoWQeR5FX
…databases Fill the database picker from the connection that just opened
A user held the 1.26 card view against the old report and named Server Context and Summary as the sections the report did better (#540). He was half right: the summary counts earn their chips, the critical count being the one red thing on screen, but the seven neutral context facts were chips too, and seven facts in identical pills make a row to decode where the report had a block to scan. A chip earns its keep by popping out of a row; a fact that is supposed to be there pops nothing. The facts are labelled rows again - Hardware, Instance, Database - muted label, full-strength value, the same convention the runtime summary uses. The pills that remain from the context are the settings that deviate from a healthy default (RCSI, stats off, scoped configs), on their severity colours, which is the one job a pill has. Summary chips and the warning-type tags stay as they were. The new test also pins the backfilled backgrounds on these rows: the press-anywhere test's fixture carries no ServerContext, so without it these are the one set of blocks the glyph-only hit-test trap could quietly reclaim. Fixes the remaining half of #540. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sr43FouJ6RizUyoWQeR5FX
…acts Give the advice header's context facts their labels back
…s/dev/signpath/github-action-submit-signing-request-3 ci: bump signpath/github-action-submit-signing-request from 2 to 3
…nViewer.App/dev/patch-and-minor-4858b7243e deps: Bump the patch-and-minor group with 9 updates
Nine package lines move together, because none of them builds without the others: the App's Avalonia core, Desktop, Themes.Fluent, Fonts.Inter and Controls.DataGrid to 12.1.2, AvaloniaEdit and AvaloniaEdit.TextMate to 12.0.0 (no 12.1.x exists), ScottPlot.Avalonia to 5.1.59, and the test project's Avalonia.Headless to 12.1.2. Nothing loud enforces that atomicity: every nuspec floor is minimum-only, so a partial bump restores and compiles green and then dies at the first HeadlessUnitTestSession.StartNew with an Avalonia-internals TypeLoadException. If that signature ever appears, check the two csprojs before suspecting anything else. TopLevel.PlatformSettings is removed in 12, and it is the only hard compile break in the whole project. The replacement is the VisualExtensions method, whose using was already present; it returns null when detached exactly as the old null-conditional chain did, so the Control fallback keeps its meaning. Baseline before the bump, for the zero-deleted gate: 713 tests total, 712 passed, 1 skipped (the pre-existing non-Windows Entra contract), 0 failed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sr43FouJ6RizUyoWQeR5FX
TextBox.Watermark is obsolete in 12 in favour of PlaceholderText, across 12 XAML sites and one code-behind constructor. The literal Watermark= rename is only safe because the app has no UseFloatingWatermark site; that one renames to UseFloatingPlaceholder, so nobody should generalize this recipe. The proxy input's parameter and the two prose comments that talk about "the watermark" move with it, because a control named one thing and described as another is how the next reader loses an afternoon. The CS0618 pragma in MainWindow wrapped nothing but a string array: the drag/drop migration moved the deprecated calls out from under it some time ago, and in 12 the APIs its comment names do not exist at all. Three comments stated mechanisms that 12 changed: - The ScottPlot line carried a do-not-bump-yet gate pointing at this very migration. It is executed, so the gate goes rather than being reworded. - The SkiaSharp pin's comment claimed it prevents issue #139. It never could: the collision is ScottPlot's NoDependencies package shipping its own libSkiaSharp.so, a different package id that a PackageReference on this one cannot constrain. Under 12 the pin is inert on top of that, since Avalonia.Skia already floors the same id at the same version. The comment now says what is true and why the pin stays anyway. - DataGrid 12.0.1 (PR #241) moved AllowAutoHide propagation out of OnApplyTemplate and into a template binding on each PART_ bar. The binding reads the same attached property off the same object, so App.axaml's rule still reaches the grids, but the reason it works is no longer the one the comment gave. The one-line assertion added to the DataGrid scrollbar test is the only falsifiable guard on the GetPlatformSettings migration: a manual Ctrl+C on Windows cannot tell a dead lookup from a live one, because the fallback the guard uses is Control and Control is what Windows reports. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sr43FouJ6RizUyoWQeR5FX
Three things in HeadlessUi were written against 11 and are no longer true of the package it wraps. The reason for hand-rolling instead of using Avalonia.Headless.XUnit has expired without the conclusion changing: the package asked for xunit v2 when this was written and asks for xunit.v3.extensibility.core 3.2.2 now, which unifies upward against this project's 4.0.1. Adopting it is possible and still wrong, because [AvaloniaFact] offers neither the #474 drain nor the canary. Leaving the dead objection in place would have let the next reader check it, find it false, and switch on that basis. The #474 note claimed a mechanism 12 half-fixed. The FontManager is still disposed one line before ResetForUnitTests runs the queue, so the throw is exactly as available as it was and the drain is still the only thing that prevents it. What 12 changed is the aftermath: scope disposal moved into a finally and the failure is routed to the dispatch's task, so one test dies instead of every test after it. Stated that way round because the original prescription, read quickly, invites deleting the drain. Which leads to the guard on GetResult(). 12 reports a teardown failure through the dispatch's own task, awaited outside both catches, so it arrived as a throw from Dispatch rather than as a returned failure. Probed under 12.1.2 rather than reasoned about: a queued job that throws during teardown comes out of GetResult(), and when the body had already failed its assertion, the teardown exception is what the caller sees and the assertion message is gone. That inverts the precedence Run documents, so the wrap keeps the body's failure first. The metrics paragraph is new and is the one place this branch explains why layout numbers across six test files moved: 12 drops headless's stub shaper for real HarfBuzz over its own embedded font, which makes a character one em wide instead of a flat 10 DIP and a line 1.0898 em tall instead of 0.8. Every re-measured threshold carries its number and points here for the reason. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sr43FouJ6RizUyoWQeR5FX
Two assertions and four comments, all on the width axis, all re-measured rather than relaxed. At a 1520px viewport the session toolbar's overflow menu now holds four entries instead of three. The fourth is QS Overview, which is the next name in CollapseOrder, and no protected command moved: Connect, Execute and Execute-with-estimate are all still on the row, and every button in front of the tail is still at the same X. That is a wider row, not a different one. The numbers behind it, measured at 12.1.2: the row's natural width is 2116px where it was 1910, and the uncollapsible floor -- Connect, the server label, the database picker and the two plan verbs -- is 746px where it was 708. Both are about 1.1x, which is what FontSize / 10 comes to at the toolbar's 11px text. The chevron's 28px height did not move, because nothing about it is text. The DataGrid scrollbar comment is corrected on both halves, since both were wrong in different directions. The mechanism half described DataGrid assigning AllowAutoHide in code, which PR #241 replaced with a template binding in 12.0.1 -- the app's rule still reaches the bars, for a different reason than the comment gave. The premise half said the rows measure zero-high for want of a font. Under 12 there is a real font with real metrics and the rows still do not realize, so the comment now says what actually stops them, which is that the grid generates no columns at all. That is pre-existing and left alone here; this commit corrects the explanation, not the test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sr43FouJ6RizUyoWQeR5FX
The rewritten pin comment asserted that the NoDependencies package overwrites the right .so at publish time. That is the shape of issue #139, but it is not what this graph does, and a comment that states a failure mode as current when the branch's own publish check disproves it is the same defect the rest of this commit series is removing. Measured instead of assumed: a linux-x64 self-contained publish puts a libSkiaSharp.so that is SHA256-identical to SkiaSharp.NativeAssets.Linux 3.119.4's copy and different from the NoDependencies 3.119.0 one, alongside managed SkiaSharp 3.119.4. The deps file names only the 3.119.4 package as the native provider, though NoDependencies is still in the restore graph underneath ScottPlot. The point the comment exists to make is unchanged: the pin is not what decides that, because it cannot reach the other package id. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sr43FouJ6RizUyoWQeR5FX
The rewritten header said the row is narrower than either harness figure on real fonts. Nothing here measures that. The 1536-logical story is what the file is about and stands on its own; the two numbers are harness thresholds, so say that instead. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sr43FouJ6RizUyoWQeR5FX
The metrics paragraph gave 1.0898 em and then four whole-DIP measurements, which do not multiply out unless you know layout rounds the desired height up. Say that, so the next reader checking 1.0898 x 12 against the 14 in a test does not conclude one of them is wrong. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sr43FouJ6RizUyoWQeR5FX
Avalonia 12 hosts a Window inside a TopLevelHost, so a Window is no longer the root of its own visual tree. GetParentWindow read VisualRoot and cast it, which now always misses, and it threw "No parent window" for every dialog a query session owns: the connection dialog, the format dialog, the plan dialogs and the open-in-editor overwrite prompt. Three tests caught it; opening any of those four in the app would have. This is the second half of the same breaking change the DataGridBehaviors fix handled, and the more dangerous half. TopLevel.PlatformSettings was deleted, so the compiler stopped the build and the migration probe saw it. VisualRoot still exists, still compiles, and still returns an object -- just not the one the cast wants -- so nothing reports it until something asks for a window at runtime. Worth remembering the next time a green build is mistaken for a migrated one. TopLevel.GetTopLevel is what the rest of the app already uses for this, at thirteen sites including QueryStore.cs and QueryStoreHistoryControl, and it still resolves to the Window under the new hosting. Measured rather than assumed on an attached session: the visual chain now ends MainWindow < TopLevelHost, while GetTopLevel and FindAncestorOfType<Window> both return MainWindow and the clipboard still resolves through it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sr43FouJ6RizUyoWQeR5FX
…ctor Two comment strengthenings the numbers alone do not carry. The overflow assertion now records what is invariant versus what is merely current. How many commands sit in the menu at a given width follows the harness's text metrics and is expected to move with them. Which commands may leave at all does not: Connect, Execute and Execute-with-estimate are absent from CollapseOrder and must never appear in that menu at any width. The exact-collection assertion is what enforces it, which is the reason it stays an exact collection rather than a count. The metrics paragraph now says why the height factor is 1.36x and not the 1.25x someone will reach for: 1.25 is the em box alone, and this face carries a line gap on top of it. The font manager reports ascent 819, descent 205 and gap 92 over an em of 1024, which are the OS/2 typographic values rather than the hhea pair that would have meant 0.854 em. So 1.36x is the tell for a height that moved, and a height that moved by anything else has a different cause. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sr43FouJ6RizUyoWQeR5FX
Avalonia 12's headless bootstrap loses a race often enough to fail one arbitrary test in roughly a quarter of full-suite runs. It throws from Dispatcher.VerifyAccess while EnsureIsolatedApplication builds the per- dispatch Application: AvaloniaHeadlessPlatform.Initialize constructs a Compositor, and DefaultRenderLoop.Add finds a different thread owning the dispatcher. No code of ours appears above HeadlessUi.Dispatch in the stack. Measured before writing anything: 2 of 6 full-suite runs on this bump, 1 of 6 more with the two classes that were red for an unrelated reason excluded, and 1 of 6 with xunit parallelization disabled outright, which is what rules out test concurrency as the cause. The same measurement on 11.3.22 was 0 of 6. A different test is the victim every time. There is no newer Avalonia to take. So the dispatch is retried once, and only when the dispatched delegate was never entered. That is the whole safety argument: this does not re-run a test, it starts one that never ran. The started flag is the first statement inside the delegate, so "the body never began" is a fact rather than an inference, and the exception match is narrow on purpose -- the type AND one of the two upstream frames -- so a thread-affinity bug in our own code names our own frames and gets reported rather than papered over. One retry only; twice in a row and the run goes red. Every occurrence writes a line to stderr, because a retry nobody can see is how a 1-in-4 flake turns into a 1-in-400 mystery. Verified against the real race rather than a simulation of it: over twenty consecutive full runs the marker appeared three times and no run failed. Ten consecutive runs through dotnet test, the way CI runs it, were green at 713 tests. Remove it when an Avalonia release fixes the race. The removal condition and the evidence rates are in the doc comment, along with the ten-run check that makes deleting it falsifiable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sr43FouJ6RizUyoWQeR5FX
The retry's stderr line now points at the opened issue (#544) instead of a placeholder, and the harness doc's three metrics paragraphs are three sibling paragraphs rather than one nested inside another. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sr43FouJ6RizUyoWQeR5FX
Both read-only schema viewers installed a TextMate installation at construction and never disposed it. An installation owns a tokenization model whose thread roots itself against collection, so every schema tab ever closed leaked a live thread for the life of the process. Both now run the query editor's own lifecycle: install on attach (guarded so a re-attach after a dispose reinstalls), dispose and null on detach. Detach fires on tab switches as well as closes, which is exactly the dance the query editor has always survived, on 11.4.1's reuse-the-disposed-transformer quirk today and on 12's explicit ownership when the upgrade lands. Fixes #546 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sr43FouJ6RizUyoWQeR5FX
…dispose Dispose the schema viewers' TextMate installations
Avalonia 12 completes a style-applied animation when its visual detaches - a handler its changelog does not document, outside the PlaybackBehavior pause guard - and re-attaching re-applies the style without resurrecting the finished animation. Switch tabs while a fetch or capture is running and its indeterminate bar comes back frozen mid-track, indistinguishable from a hang. Avalonia 11 had no detach handling at all, so the bar just kept sweeping. ProgressBarBehaviors.RestartOnReattach deactivates and reactivates the :indeterminate style one dispatcher hop apart on reattach, which spawns a fresh animation instance. Applied to all seven bars whose hosts detach mid-run: the two execution overlays, the actual-plan loading tab, and the QS grid, wait-stats, QS history and Overview loaders. Proven against the live symptom in a driven run, both directions: five window captures spanning a full animation cycle after a tab round-trip were pixel-identical without this, and sweep again with it. The headless suite cannot see animations, so that driven pair is the regression evidence. This was the plan's pre-classified bucket (b) item for the manual pass, found exactly where the review predicted. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sr43FouJ6RizUyoWQeR5FX
Upgrade to Avalonia 12
No app code ever referenced it: the package ships its own RegistryOptions and ThemeName under the AvaloniaEdit.TextMate.Grammars namespace, and every call site binds the TextMateSharp.Grammars pair instead, which arrives through the TextMateSharp.Grammars 2.0.4 reference that stays. Dropping it also removes Newtonsoft.Json 13.0.1 and the stale TextMateSharp 1.0.23 floor from the resolved graph. Kept out of the Avalonia 12 migration deliberately (a runtime-graph change had no place in that release); shipping now as its own change. Fixes #545 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sr43FouJ6RizUyoWQeR5FX
AccessText takes the character after the first underscore as an access key and strips the underscore from display, so "Treat [ ] _ as literals" rendered without the one character that is half of what the checkbox is about. Doubling it is the escape: __ renders as a literal underscore and registers no access key. Fixes #547 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sr43FouJ6RizUyoWQeR5FX
The existing DataGrid case turns out to assert AllowAutoHide on bars that never apply their template, because the grid it builds has no columns and Avalonia's DataGrid does not auto-generate them — so no row realizes and nothing overflows. The bound flag it checks is still meaningful, and it stays exactly as it was; its construction comment now says what is actually happening instead of claiming a real grid. The new sibling gives a grid columns, makes it overflow both ways, and pins the state every grid in the app actually runs in: templated bars exist, each keeps AllowAutoHide from the attached property the App.axaml rule sets (12 binds it in-template), and the vertical bar is visible. Zero templated bars realize without columns, so the NotEmpty assertion alone separates the two cases. Fixes #548 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sr43FouJ6RizUyoWQeR5FX
…ckage Drop the unused AvaloniaEdit.TextMate.Grammars package
…label Let the escape-brackets label keep its underscore
…-test Pin the scrollbar contract on a grid whose bars actually realize
The attach handler focused the editor synchronously, mid-event, while the tab strip and window were still arbitrating focus - so whatever ran after the handler won, and the first keystrokes into a fresh query tab landed nowhere until something was clicked. The same in a real window on Avalonia 11 and 12; invisible headlessly, because no arbitration runs there. Focusing one Loaded-priority hop later lands after the layout pass and sticks. Focus() on a control whose tab was switched away before the post fires is a benign no-op in both Avalonia lines (no visual root means no focus manager means false, not a throw), so the deferral needs no guard. The other FocusEditor call sites run from input handlers after arbitration settles and stay synchronous. Drive-verified both directions: fresh launch, Ctrl+N, immediate typing - lost before the change, colorized in the editor after it. Fixes #551 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sr43FouJ6RizUyoWQeR5FX
Let typing follow Ctrl+N without a click
IsFunctionOnColumnSide split the whole predicate at its first comparison operator. In a compound predicate that put every later comparison, column and all, on the function's side, so [t].[A]=CONVERT_IMPLICIT(int,[@1],0) AND [t].[B]=CONVERT(tinyint,[@2],0) reported a conversion of a parameter as a function on a column. The same mistake fired on the everyday date range [d]>=dateadd(day,(-7),getdate()) AND [d]<getdate(). The side check now reads only the comparison that holds the function: the text between the nearest AND/OR on either side, split at every depth so parenthesized OR groups come apart too, and never inside a string literal or a bracketed name. Two more false positives from the same family, fixed with it: - ISNULL/COALESCE was flagged wherever it appeared. It now gets the same side check, so ISNULL(@p, 0) on the parameter side is quiet while col = ISNULL(@p, col) is still caught, and a parameter-side ISNULL no longer steals the message from a real function on a column. - LIKE was not recognized as a comparison, so [col] like upper([@p]) fell to the assume-the-worst default and was flagged. DetectNonSargablePattern is split out as an internal seam so predicate shapes can be tested as raw strings, the way #436 tested ConvertImplicitWrapsColumn. The reporter's plan is a new fixture. Both golden masters change only by that fixture: the warning digest gains its section (a true Scan With Predicate, no Non-SARGable), and the comparison digest gains its self and next pairings plus the one predecessor pairing that now points at it. Fixes #556 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E9G1hWs6Nk4h4U2gn7TSy5
…te-sides Read only the function's own comparison in the non-SARGable check
Rule 15 matched the operator shape alone. WHERE t.A IN (@p1, @p2) on an indexed column builds the same Nested Loops / Merge Interval / TopN Sort / Concatenation / Constant Scan chain as a dynamic seek over the parameter values, so a query with no join was told to rewrite as UNION ALL (#558). The rule now also reads what each lookup branch produces, and fires only when at least one branch takes a value from another input: an outer column, or an expression the outer input computed ([Expr1002] for ON t.A = o.X + 1 OR ...). Branches of parameters, variables, literals and functions of them are a parameter list. A branch with nothing to read keeps the warning. Fixtures: the reporter's plan, plus a mixed column/parameter OR join and an OR join on outer expressions, both captured on SQL Server 2022. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ZVrq8tpA2DBPEFEqahFK6
…r-in-list Join OR Clause: skip the dynamic seek for a list of parameters (#558)
A plan from the plan cache or Query Store records an sp_executesql statement with its declaration list in front: "(@p1 int, @p2 int)SELECT". Substitution read those names as references, so the advice, exports, MCP tools, "Copy Query Text (with values)" and planview's text output showed "(10 int, 20 int)SELECT ...", which neither matches the plan nor runs. Only the statement after the list gets values now, and when it gets any, the list is dropped. With no values the text stays as the plan recorded it. A list that never closes (the plan cut the text off at 4,000 characters inside it) is left alone. parameterized_statement_text still carries the original. ReproScriptBuilder had its own copy of the list parser; both now use ParameterSubstitution.DeclarationListEnd, which the web project can see. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ZVrq8tpA2DBPEFEqahFK6
…ation-list Leave the sp_executesql declaration list out of substituted statement text
Expensive Operator compared one operator's own time against the whole statement's elapsed time and fired at a 20% share. In a statement that finishes in under a second, one or two operators always take most of the (tiny) time just because there is almost nothing else to divide it among, so the share pointed at nothing. Add a 1,000ms floor on statement elapsed time before the rule runs, matching the floor rule 19 uses for compile CPU and rule 4 uses to call UDF time Critical. Closes #562
…riable column Rule 12 relies on ColumnReferenceRegex, which requires a dotted name ([table].[col]) to recognize a column. An aliased table-variable column renders dotted through the alias (e.g. "abs(@tv.[X] as [v].[X])=(1)"), so that case already worked. An unaliased one renders as a bare bracketed name with no dotted qualifier at all (e.g. "abs([X])=(1)"), confirmed on SQL Server 2016, 2017, 2019, 2022 and 2025. The regex cannot see that as a column on its own, since the same shape could be a parameter or an expression, so the function call and the implicit conversion in the fixtures below never got flagged. Add IsTableVariable(node) and thread an isTableVariableScan flag from DetectNonSargablePredicate through DetectNonSargablePattern into a new shared IsColumnReference helper, used by both ConvertImplicitWrapsColumn and IsFunctionOnColumnSide. On a confirmed table-variable scan, a bare name now also counts as a column, unless it is a parameter or variable, an optimizer-generated expression, or a function call. Every other scan keeps today's behavior, since the flag defaults to false. Also corrects two comments that claimed an unaliased table-variable column renders as [@tv].[col] — no SQL Server version does this. Closes #561
The rewritten ColumnReferenceRegex comment said an aliased table-variable column failed the old pattern. It did not: the alias part [v].[X] has no @, so the first cut matched it. The [@tv].[col] shape that the first cut was blamed for never occurs on SQL Server 2016 through 2025, so the history goes and the comment states only what each version renders. IsColumnReference now names the case it still reads wrong: a bare outer reference from another unaliased table variable (#564). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ZVrq8tpA2DBPEFEqahFK6
…or-floor Require 1,000ms statement elapsed before rule 35 fires
…are-columns Detect a non-SARGable function or conversion on an unaliased table-variable column
…ility Rule 12 counted every column-shaped name in a scan predicate as the scanned table's own column, including a value a Nested Loops join passed in from its outer input one row at a time. A function or conversion wrapping only that outer reference does not cost the scan a seek, so it should not be flagged. DetectNonSargablePredicate now builds a ScanIdentity (alias, table, whether it is a table variable, and the bare outer reference names a Nested Loops ancestor passes in) and threads it down through IsColumnReference, which checks ownership by alias or table instead of just matching any dotted or bare name. Closes #564 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ZVrq8tpA2DBPEFEqahFK6
The parser cleans a temp table's full tempdb name (#t, underscores, then a hex suffix) down to #t in the scan's ObjectName. The unaliased owner in the predicate was compared raw, so a plan that spells the full name in both places would lose a real warning on the temp table's own column. CleanTempTableName is now internal and the owner goes through it too. Two string tests pin it: the scan's own column under its full name is flagged, and another temp table's full name is not. Removing the cleaning fails the first one. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ZVrq8tpA2DBPEFEqahFK6
…columns Skip outer references when checking scan predicates for non-SARGability (#564)
The v1.27.0 Release run did not sign. Action v3 sends requests to SignPath's new Pipeline Connector, which needs the SignPath GitHub App on the repository, and SignPath signs only the first 3 attempts of a run. The app is now installed. release.yml: note the app requirement and the 3-attempt limit above the signing step, and correct the timeout comment, which still said a failed signing leaves a published release. release-signing.yml: note that the Pipeline Connector reads this file only when the signing policy references it as a Pipeline Policy. Correct the reasons for leaving out build.disallow_reruns (the release is now created after signing) and branch_rulesets (main now has the "Protect main" ruleset). Comments only. Part of #569. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011ujjGZ64tzhVqkEBfaxe4o
Correct the SignPath comments for action v3
7 tasks
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
check-versionstatus fails on this PR, because main already has version 1.27.0 from v1.27.0 #568. This failure is expected, so merge as an admin. The version is still higher than the latest release, v1.26.0.Changes
.github/workflows/release.ymland.signpath/policies/PerformanceStudio/release-signing.yml.Test Plan
build-and-testpasses.Part of #569.
Generated with Claude Code
https://claude.ai/code/session_011ujjGZ64tzhVqkEBfaxe4o