Repository navigation
Prepare 3.7.0: version bump, changelog cut (with the missing 3.6.0 section), README tool counts - #3242
Conversation
Bumps the single Directory.Build.props version declaration to 3.7.0. Splits [Unreleased] into [3.7.0] and the missing [3.6.0] section: 3.6.0 shipped from the 2026-08-27 dev merge without its changelog header being cut, so Unreleased had accumulated two releases. The partition follows the v3.6.0 tag's own Unreleased content (111 entries to [3.6.0], 264 to [3.7.0]), moved verbatim; a line-multiset comparison verified the only content changes are the two version headers, three duplicated section headers, and blank lines. Adds the Important section for 3.7.0's ten store migrations (v104 to v114), verified live against a v104 store during the nightly dogfood. README: Darling's MCP tool count (139, counted from the live nightly endpoint) joins Lite's in the comparison table and the MCP section. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AtjWJJ3NnSkDSrPBsGywoZ
|
|
||
| ### Important | ||
|
|
||
| - **The Darling store migrates from schema v104 to v114 automatically on the first service start after upgrading** - ten migrations, no operator action, verified against a live v104 store during the 3.7.0 nightly dogfood (fleet of ten targets kept collecting through the restart). The store's least-privilege roles are re-provisioned in the same start: the Viewer and the MCP host no longer connect as the store superuser, and the `mcp` role gains the server-onboarding and alert-tuning writes its tools use. Rollup-coverage retention gates may HOLD PAUSED on stores younger than their rollup horizons; they arm themselves once the daily rollups cover their windows, exactly as the log says. | ||
|
|
||
| ### Added |
There was a problem hiding this comment.
The [3.7.0] section has no entry for #3235 ("Estimate PostgreSQL index bloat from statistics instead of walking every page"), the commit immediately preceding this one on dev (84e27fe). That's a significant behavior reversal — pg_index_bloat switches from the exact pgstatindex measurement this changelog spent several entries championing (#2561, #2617, #3109, #3121, #3164, #3184: "PostgreSQL index bloat, MEASURED rather than estimated" / "Index bloat is unaffected by any grant") to a statistics-based estimate, adds a measurement_kind discriminator, and folds into the V114 migration mentioned in the Important block above. Given every other PR in this history gets a detailed entry, and this one flips a design decision documented at length elsewhere in the same file, its absence looks like an oversight in the Unreleased→3.7.0 cut rather than a deliberate omission.
|
Reviewed the diff (CHANGELOG.md, Directory.Build.props, README.md — no T-SQL or code changes in this PR, so the T-SQL style/parity checks don't apply to source; checked the README's cross-SKU claims for accuracy instead). Verified as correct:
One gap worth checking before merge: left as an inline comment — the new |
…e fix wave Takes dev's CHANGELOG wholesale and re-runs the byte-preserving split: the wave's 14 entries land under [3.7.0] - 2026-09-10 (278 entries), [3.6.0] unchanged at 111, entry multiset verified unchanged, census CRLF-only. README auto-merged (tool counts coexist with the wave's PostgreSQL rewrites). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dqza1QNL4YCFS1MpzezERn
|
|
||
| ### Added | ||
|
|
There was a problem hiding this comment.
Nit: this PR adds a blank line after ### Added/### Changed/### Fixed throughout the new [3.7.0] and [3.6.0] sections, but older sections (e.g. [3.5.0] at line 430) go straight from the header to the first bullet with no blank line. Not a big deal since CONTRIBUTING.md doesn't specify a changelog format, but it's a formatting inconsistency introduced by this PR (confirmed it wasn't present pre-PR) that future changelog entries will inconsistently follow depending on which section they copy from.
|
Reviewed — this is a content-only PR ( Verification performed:
One minor nit (left as an inline comment): the new No correctness, security, or performance concerns given the scope of the diff. 🤖 Generated with Claude Code |
…-CPU work, re-split changelog Rebuilds the [3.7.0]/[3.6.0] split from dev's corrected CHANGELOG (carries #3271); byte-preserving, CRLF-only, entry multiset unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WejwpgWF5Xfm3fAmoEbFz4
| @@ -413,6 +425,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| - **Azure SQL Database gets tempdb monitoring back, and the reason it lost it was checkably false** ([#2512]) - `TempDbStatsCollector.AppliesTo` was `!target.IsAzureSqlDb`, so the entire tier - General Purpose and Hyperscale alike - collected **no tempdb data at all**: no `get_tempdb_trend`, no tempdb space alerting, nothing in the viewer's tempdb surfaces, on a platform where the customer cannot go and look at the box themselves. The doc comment justified that by saying the first result set's three-part `tempdb.sys.dm_db_file_space_usage` reference could not be served there and the collector "could only ever fail". Running the collector's SQL verbatim against live Azure SQL Database says otherwise: on `GP_S_Gen5_2` (EngineEdition 5, 12.0.2000.8) it returns user 5.44 MB / internal 1.81 MB / version store 0.00 MB / unallocated 54.19 MB plus one session over threshold, on `HS_S_Gen5_2` user 1.88 MB / unallocated 60.69 MB, and `SELECT COUNT(*) FROM tempdb.sys.dm_db_file_space_usage` returns 4 on both. **Both** result sets bind, so the whole row this collector promises is available - the second one (`sys.dm_db_session_space_usage`) was never in doubt. The numbers also *describe* something rather than merely returning: allocating ~57 MB into a `#temp` table moved `user_mb` 1.88 -> 59.75 and `unallocated_mb` 60.69 -> 2.69, with the session view attributing 59.25 MB to the session that did it. And it is worth **more** here than on a box, because the tempdb ceiling on Azure SQL Database is governed by the SERVICE TIER: you cannot add files and cannot grow past the cap, so "tempdb is filling" means "change service objective", not "go look at the disk". **Managed Instance is unchanged** - it was never gated, it has a real tempdb, and the pin asserts both directions so this cannot be re-narrowed on the strength of a stale comment. What the #2150 field report was actually about is PERMISSIONS, and that is now handled where it belongs: error 262 ("permission denied in database 'tempdb'") was classified `Unclassified`, which means **ERROR every cycle forever** - so a login that cannot read the DMV now degrades to PERMISSIONS with the same kind of explanatory Azure sentence error 300 already gets, rather than polluting collection health. The number set behind that classification lived in **six** hand-maintained places and had already drifted three different ways (916 was in `SqlServerTargetProvider.Classify` and the two failed-jobs msdb reads but in neither collector catch; 8189 was in the collector catches but in neither failed-jobs read; Lite's XE-session catch had neither); all six now read one shared `SqlServerPermissionErrors.IsPermissionDenied`, which is also the only way to pin them, since `SqlException` cannot be constructed in a test. **One thing to know before turning tempdb alerting on for Azure targets**: `unallocated_mb` is free space in the tempdb files *as currently allocated*, and Azure SQL Database creates them small (4 files, ~62 MB total on both 2-vCore tiers measured) and autogrows toward the tier cap - so the `reserved / (reserved + unallocated)` ratio the `tempdb Space` alert fires on measures distance to the next autogrow, not distance to the ceiling. The measurement above takes that ratio from 3% to 96% with a single `#temp` table, against an 80% default. The absolute MB and the trend are the honest signal on this tier; giving the ratio a size floor (the shape `ScorePlanCacheBloat` already uses for the same reason) is filed as [#2515] rather than retuning a threshold every existing on-prem target depends on. Applies to Lite and Darling, which share the collector definition, the classifier and the alert engine | |||
| - **Lite's .NET runtime prerequisites were documented against the wrong artifact** ([#2489]) - the README hung "requires .NET 10 Desktop Runtime" on the **`Setup.exe`** download line. That is the one artifact with no prerequisite at all: it is packed by `vpk` from a `--self-contained -r win-x64` publish, and its `runtimeconfig.json` carries `includedFrameworks`, not `frameworks`. Meanwhile the portable **ZIP** - a framework-dependent publish whose `runtimeconfig.json` names **three** shared frameworks - had its prerequisites written down nowhere, and `Lite/README.md` had no prerequisites section at all. So a tester who read the one runtime sentence we published installed a runtime they did not need, while a tester who unzipped onto a stock Windows Server got the .NET host's bare `You must install .NET to run this application` with nothing of ours on it. Each artifact now carries its OWN prerequisites, in the root README and in a new **Prerequisites** section in `Lite/README.md`. **ASP.NET Core Runtime 10 is required unconditionally** for the ZIP, and that is the part nobody expects: `ModelContextProtocol.AspNetCore` brings the framework reference in transitively, so it is named at build time and leaving MCP switched off does not remove it - and because the host reports only the FIRST framework it cannot find, installing just the Desktop Runtime buys a second identical failure. Lite cannot gate on this the way [#2481] did for Darling, because Lite has no install script and the host error precedes every line of our code; what ships instead is `READ-ME-FIRST.txt` beside `PerformanceMonitorLite.exe`, the one surface of ours a stranded reader can still reach. `LiteRuntimePrerequisiteDocsTests` keeps all of it honest by DERIVING the claims - the required runtimes from the built `runtimeconfig.json`, and which artifact is self-contained from the `dotnet publish` lines in `build.yml` and `nightly.yml` - so dropping the MCP package, making the ZIP self-contained, or moving to a new .NET major turns the docs red instead of stale. `README.md` and `Lite/README.md` are named in build.yml's `lite` path filter for the same reason: markdown is carved out of every area filter and a docs-only PR skips .NET setup entirely, so the guard would otherwise be unrunnable on precisely the change it exists to catch. | |||
|
|
|||
|
|
|||
There was a problem hiding this comment.
Nit: this adds a second blank line before ## [3.5.0], so the boundary now reads with a double blank where every other version-header boundary in this file (including the new [3.7.0]→[3.6.0] boundary this same PR introduces at line ~305) has exactly one. dev's CHANGELOG.md has only a single blank line here. Harmless, but worth a one-line fix for consistency:
| ## [3.5.0] - 2026-08-19 |
| @@ -413,6 +425,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| - **Azure SQL Database gets tempdb monitoring back, and the reason it lost it was checkably false** ([#2512]) - `TempDbStatsCollector.AppliesTo` was `!target.IsAzureSqlDb`, so the entire tier - General Purpose and Hyperscale alike - collected **no tempdb data at all**: no `get_tempdb_trend`, no tempdb space alerting, nothing in the viewer's tempdb surfaces, on a platform where the customer cannot go and look at the box themselves. The doc comment justified that by saying the first result set's three-part `tempdb.sys.dm_db_file_space_usage` reference could not be served there and the collector "could only ever fail". Running the collector's SQL verbatim against live Azure SQL Database says otherwise: on `GP_S_Gen5_2` (EngineEdition 5, 12.0.2000.8) it returns user 5.44 MB / internal 1.81 MB / version store 0.00 MB / unallocated 54.19 MB plus one session over threshold, on `HS_S_Gen5_2` user 1.88 MB / unallocated 60.69 MB, and `SELECT COUNT(*) FROM tempdb.sys.dm_db_file_space_usage` returns 4 on both. **Both** result sets bind, so the whole row this collector promises is available - the second one (`sys.dm_db_session_space_usage`) was never in doubt. The numbers also *describe* something rather than merely returning: allocating ~57 MB into a `#temp` table moved `user_mb` 1.88 -> 59.75 and `unallocated_mb` 60.69 -> 2.69, with the session view attributing 59.25 MB to the session that did it. And it is worth **more** here than on a box, because the tempdb ceiling on Azure SQL Database is governed by the SERVICE TIER: you cannot add files and cannot grow past the cap, so "tempdb is filling" means "change service objective", not "go look at the disk". **Managed Instance is unchanged** - it was never gated, it has a real tempdb, and the pin asserts both directions so this cannot be re-narrowed on the strength of a stale comment. What the #2150 field report was actually about is PERMISSIONS, and that is now handled where it belongs: error 262 ("permission denied in database 'tempdb'") was classified `Unclassified`, which means **ERROR every cycle forever** - so a login that cannot read the DMV now degrades to PERMISSIONS with the same kind of explanatory Azure sentence error 300 already gets, rather than polluting collection health. The number set behind that classification lived in **six** hand-maintained places and had already drifted three different ways (916 was in `SqlServerTargetProvider.Classify` and the two failed-jobs msdb reads but in neither collector catch; 8189 was in the collector catches but in neither failed-jobs read; Lite's XE-session catch had neither); all six now read one shared `SqlServerPermissionErrors.IsPermissionDenied`, which is also the only way to pin them, since `SqlException` cannot be constructed in a test. **One thing to know before turning tempdb alerting on for Azure targets**: `unallocated_mb` is free space in the tempdb files *as currently allocated*, and Azure SQL Database creates them small (4 files, ~62 MB total on both 2-vCore tiers measured) and autogrows toward the tier cap - so the `reserved / (reserved + unallocated)` ratio the `tempdb Space` alert fires on measures distance to the next autogrow, not distance to the ceiling. The measurement above takes that ratio from 3% to 96% with a single `#temp` table, against an 80% default. The absolute MB and the trend are the honest signal on this tier; giving the ratio a size floor (the shape `ScorePlanCacheBloat` already uses for the same reason) is filed as [#2515] rather than retuning a threshold every existing on-prem target depends on. Applies to Lite and Darling, which share the collector definition, the classifier and the alert engine | |||
| - **Lite's .NET runtime prerequisites were documented against the wrong artifact** ([#2489]) - the README hung "requires .NET 10 Desktop Runtime" on the **`Setup.exe`** download line. That is the one artifact with no prerequisite at all: it is packed by `vpk` from a `--self-contained -r win-x64` publish, and its `runtimeconfig.json` carries `includedFrameworks`, not `frameworks`. Meanwhile the portable **ZIP** - a framework-dependent publish whose `runtimeconfig.json` names **three** shared frameworks - had its prerequisites written down nowhere, and `Lite/README.md` had no prerequisites section at all. So a tester who read the one runtime sentence we published installed a runtime they did not need, while a tester who unzipped onto a stock Windows Server got the .NET host's bare `You must install .NET to run this application` with nothing of ours on it. Each artifact now carries its OWN prerequisites, in the root README and in a new **Prerequisites** section in `Lite/README.md`. **ASP.NET Core Runtime 10 is required unconditionally** for the ZIP, and that is the part nobody expects: `ModelContextProtocol.AspNetCore` brings the framework reference in transitively, so it is named at build time and leaving MCP switched off does not remove it - and because the host reports only the FIRST framework it cannot find, installing just the Desktop Runtime buys a second identical failure. Lite cannot gate on this the way [#2481] did for Darling, because Lite has no install script and the host error precedes every line of our code; what ships instead is `READ-ME-FIRST.txt` beside `PerformanceMonitorLite.exe`, the one surface of ours a stranded reader can still reach. `LiteRuntimePrerequisiteDocsTests` keeps all of it honest by DERIVING the claims - the required runtimes from the built `runtimeconfig.json`, and which artifact is self-contained from the `dotnet publish` lines in `build.yml` and `nightly.yml` - so dropping the MCP package, making the ZIP self-contained, or moving to a new .NET major turns the docs red instead of stale. `README.md` and `Lite/README.md` are named in build.yml's `lite` path filter for the same reason: markdown is carved out of every area filter and a docs-only PR skips .NET setup entirely, so the guard would otherwise be unrunnable on precisely the change it exists to catch. | |||
|
|
|||
|
|
|||
There was a problem hiding this comment.
Correction to my suggestion above: applying it would duplicate the ## [3.5.0] header (since line 429 already has it). The actual fix is just to delete this blank line, e.g.:
|
Reviewed. This is a mechanical release-prep PR (CHANGELOG.md, Directory.Build.props, README.md only — no T-SQL, no C#), so the usual Lite/Darling parity, T-SQL style, security, and performance-regression checks don't apply here. Verification performed:
One minor nit left as an inline comment: the split introduces a duplicate blank line right before the No correctness, security, or parity issues found. |
…shipped release The guard derived the expected fixture from the top changelog heading, which at a cut is the version being cut (3.7.0) - a fixture of which is already at the ladder top and climbs nothing, failing the climb theory's applied>0 assertion. It now targets the newest SHIPPED release (excluding the in-development Directory.Build.props version), i.e. 3.6.0, the store users upgrade from. migration-ladder-v3.6.0.sql generated from the v3.6.0 tag (103 rungs, top V104) so the current V114 ladder replays 10 real rungs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WejwpgWF5Xfm3fAmoEbFz4
| @@ -413,6 +425,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| - **Azure SQL Database gets tempdb monitoring back, and the reason it lost it was checkably false** ([#2512]) - `TempDbStatsCollector.AppliesTo` was `!target.IsAzureSqlDb`, so the entire tier - General Purpose and Hyperscale alike - collected **no tempdb data at all**: no `get_tempdb_trend`, no tempdb space alerting, nothing in the viewer's tempdb surfaces, on a platform where the customer cannot go and look at the box themselves. The doc comment justified that by saying the first result set's three-part `tempdb.sys.dm_db_file_space_usage` reference could not be served there and the collector "could only ever fail". Running the collector's SQL verbatim against live Azure SQL Database says otherwise: on `GP_S_Gen5_2` (EngineEdition 5, 12.0.2000.8) it returns user 5.44 MB / internal 1.81 MB / version store 0.00 MB / unallocated 54.19 MB plus one session over threshold, on `HS_S_Gen5_2` user 1.88 MB / unallocated 60.69 MB, and `SELECT COUNT(*) FROM tempdb.sys.dm_db_file_space_usage` returns 4 on both. **Both** result sets bind, so the whole row this collector promises is available - the second one (`sys.dm_db_session_space_usage`) was never in doubt. The numbers also *describe* something rather than merely returning: allocating ~57 MB into a `#temp` table moved `user_mb` 1.88 -> 59.75 and `unallocated_mb` 60.69 -> 2.69, with the session view attributing 59.25 MB to the session that did it. And it is worth **more** here than on a box, because the tempdb ceiling on Azure SQL Database is governed by the SERVICE TIER: you cannot add files and cannot grow past the cap, so "tempdb is filling" means "change service objective", not "go look at the disk". **Managed Instance is unchanged** - it was never gated, it has a real tempdb, and the pin asserts both directions so this cannot be re-narrowed on the strength of a stale comment. What the #2150 field report was actually about is PERMISSIONS, and that is now handled where it belongs: error 262 ("permission denied in database 'tempdb'") was classified `Unclassified`, which means **ERROR every cycle forever** - so a login that cannot read the DMV now degrades to PERMISSIONS with the same kind of explanatory Azure sentence error 300 already gets, rather than polluting collection health. The number set behind that classification lived in **six** hand-maintained places and had already drifted three different ways (916 was in `SqlServerTargetProvider.Classify` and the two failed-jobs msdb reads but in neither collector catch; 8189 was in the collector catches but in neither failed-jobs read; Lite's XE-session catch had neither); all six now read one shared `SqlServerPermissionErrors.IsPermissionDenied`, which is also the only way to pin them, since `SqlException` cannot be constructed in a test. **One thing to know before turning tempdb alerting on for Azure targets**: `unallocated_mb` is free space in the tempdb files *as currently allocated*, and Azure SQL Database creates them small (4 files, ~62 MB total on both 2-vCore tiers measured) and autogrows toward the tier cap - so the `reserved / (reserved + unallocated)` ratio the `tempdb Space` alert fires on measures distance to the next autogrow, not distance to the ceiling. The measurement above takes that ratio from 3% to 96% with a single `#temp` table, against an 80% default. The absolute MB and the trend are the honest signal on this tier; giving the ratio a size floor (the shape `ScorePlanCacheBloat` already uses for the same reason) is filed as [#2515] rather than retuning a threshold every existing on-prem target depends on. Applies to Lite and Darling, which share the collector definition, the classifier and the alert engine | |||
| - **Lite's .NET runtime prerequisites were documented against the wrong artifact** ([#2489]) - the README hung "requires .NET 10 Desktop Runtime" on the **`Setup.exe`** download line. That is the one artifact with no prerequisite at all: it is packed by `vpk` from a `--self-contained -r win-x64` publish, and its `runtimeconfig.json` carries `includedFrameworks`, not `frameworks`. Meanwhile the portable **ZIP** - a framework-dependent publish whose `runtimeconfig.json` names **three** shared frameworks - had its prerequisites written down nowhere, and `Lite/README.md` had no prerequisites section at all. So a tester who read the one runtime sentence we published installed a runtime they did not need, while a tester who unzipped onto a stock Windows Server got the .NET host's bare `You must install .NET to run this application` with nothing of ours on it. Each artifact now carries its OWN prerequisites, in the root README and in a new **Prerequisites** section in `Lite/README.md`. **ASP.NET Core Runtime 10 is required unconditionally** for the ZIP, and that is the part nobody expects: `ModelContextProtocol.AspNetCore` brings the framework reference in transitively, so it is named at build time and leaving MCP switched off does not remove it - and because the host reports only the FIRST framework it cannot find, installing just the Desktop Runtime buys a second identical failure. Lite cannot gate on this the way [#2481] did for Darling, because Lite has no install script and the host error precedes every line of our code; what ships instead is `READ-ME-FIRST.txt` beside `PerformanceMonitorLite.exe`, the one surface of ours a stranded reader can still reach. `LiteRuntimePrerequisiteDocsTests` keeps all of it honest by DERIVING the claims - the required runtimes from the built `runtimeconfig.json`, and which artifact is self-contained from the `dotnet publish` lines in `build.yml` and `nightly.yml` - so dropping the MCP package, making the ZIP self-contained, or moving to a new .NET major turns the docs red instead of stale. `README.md` and `Lite/README.md` are named in build.yml's `lite` path filter for the same reason: markdown is carved out of every area filter and a docs-only PR skips .NET setup entirely, so the guard would otherwise be unrunnable on precisely the change it exists to catch. | |||
|
|
|||
There was a problem hiding this comment.
Nit: this leaves a double blank line before ## [3.5.0] (lines 427-428), while every other version-heading boundary in the file (e.g. before ## [3.6.0] at line 305, before ## [3.7.0] at line 8) has exactly one blank line. Looks like a stray line left over from the byte-preserving split rather than an intentional separator.
Review summaryReviewed this as a release-prep PR: version bump, changelog re-split, README tool-count update, and a migration-ladder guard fix + generated fixture. No T-SQL collector code changed, so the SQL style conventions (AS aliasing, Checked and looks correct:
One nit left as an inline comment: a leftover double blank line before Nothing else stood out — this is a low-risk, mechanical release-prep PR. |
Release-prep for 3.7.0, from the nightly-dogfood session. Not the release itself: this merges to dev, and the dev-to-main release PR comes after validation finishes.
What's here
Directory.Build.props3.6.0 -> 3.7.0 (the one line, per Four shipped projects carry independent <Version> literals; the release gate reads a deprecated one and the nightly reads a different one #3222).[Unreleased]split into[3.7.0] - 2026-09-10(264 entries) and the missing[3.6.0] - 2026-08-27(111 entries) — 3.6.0 shipped without its header being cut, so Unreleased had two releases in it. The partition follows the v3.6.0 tag's own Unreleased content, entries moved byte-verbatim; a normalized line-multiset diff confirms the only changes are two version headers, three duplicated section headers, and blank lines. A new Important block documents the automatic v104 -> v114 store migration (ten migrations) and the role hardening, both verified live during the dogfood upgrade of DARLING01.Dogfood status feeding this release
build,Darling PostgreSQL tests,linux).Before the cut (left deliberately undone here)
🤖 Generated with Claude Code
https://claude.ai/code/session_01AtjWJJ3NnSkDSrPBsGywoZ