Skip to content

Prepare 3.7.0: version bump, changelog cut (with the missing 3.6.0 section), README tool counts - #3242

Merged
erikdarlingdata merged 4 commits into
devfrom
release/v3.7.0
Sep 10, 2026
Merged

erikdarlingdata merged 4 commits into
devfrom
release/v3.7.0

Conversation

@erikdarlingdata

Copy link
Copy Markdown
Owner

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

  • Version: Directory.Build.props 3.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).
  • Changelog: [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.
  • README: Darling's MCP tool count (139, counted from the running nightly's tools/list) added beside Lite's 87 in the comparison table and MCP section. Collector counts (69 / 27 PostgreSQL) were already current on dev.

Dogfood status feeding this release

Before the cut (left deliberately undone here)

🤖 Generated with Claude Code

https://claude.ai/code/session_01AtjWJJ3NnSkDSrPBsGywoZ

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
Comment thread CHANGELOG.md

### 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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@claude

claude Bot commented Sep 10, 2026

Copy link
Copy Markdown

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:

  • Directory.Build.props is the single version source (per its own header doc) and is the only place 3.6.0 needed to become 3.7.0 — no other stale version string found in .props/.csproj/.json/.ps1/.yml.
  • README tool counts check out exactly against source: Lite 87, Darling 139 (grep -ohP '\[McpServerTool\(Name = "\K[a-z_0-9]+' ... | sort -u | wc -l), and all 52 Darling MCP tool classes carrying [McpServerToolType] are wired into DarlingMcpHostService's AddMcpServer chain, so the static count matches what's actually exposed.
  • CHANGELOG section boundaries, dates, and reference-style link definitions ([#xxxx]: url) all resolve — no dangling or duplicate links found in the new [3.7.0]/[3.6.0] split.

One gap worth checking before merge: left as an inline comment — the new [3.7.0] section has no entry for #3235 (PostgreSQL index bloat estimation, merged as the immediately-preceding commit on dev), even though it's a real behavior reversal from the "MEASURED rather than estimated" stance this changelog documents at length elsewhere, and it's folded into the V114 migration the Important block already mentions.

…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
Comment thread CHANGELOG.md

### Added

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@claude

claude Bot commented Sep 10, 2026

Copy link
Copy Markdown

Reviewed — this is a content-only PR (CHANGELOG.md, Directory.Build.props, README.md), so there's no T-SQL, no Lite/Darling code, and no Lite/Darling parity surface to check.

Verification performed:

  • Changelog split integrity: independently extracted the set of #NNNN issue references removed from the Added/Changed/Fixed sections that used to sit under Unreleased (now [3.7.0]) and compared it against the set added to the new [3.6.0] - 2026-08-27 section. Both sets are exactly 60 unique issue numbers and match — nothing was dropped or duplicated in the split, consistent with the PR description's claim of a byte-verbatim move.
  • Version bump: Directory.Build.props 3.6.0 → 3.7.0 is the only version-declaration file that needed touching — Darling/Darling.Tests/ProductVersionDeclarationTests.cs and Lite.Tests/AgedDatabaseMigrationTests.cs reference "3.6.0" but only in doc comments/history notes, not as hardcoded assertions, so the bump won't break those pinned tests.
  • README: Darling's tool count (139) is updated consistently in both the comparison table and the MCP section; Lite's 87 and Dashboard's 66 are left untouched and still match elsewhere in the file.

One minor nit (left as an inline comment): the new [3.7.0]/[3.6.0] sections have a blank line after each ### Added/### Changed/### Fixed header, which existing sections like [3.5.0] don't have. Purely cosmetic — CONTRIBUTING.md has no changelog formatting rule — but worth knowing it's a style drift introduced here.

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
Comment thread CHANGELOG.md
@@ -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.


Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

Suggested change
## [3.5.0] - 2026-08-19

Comment thread CHANGELOG.md
@@ -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.


Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.:

Suggested change

@claude

claude Bot commented Sep 10, 2026

Copy link
Copy Markdown

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:

  • Version bump: Directory.Build.props 3.6.0 → 3.7.0, single-line change, correct.
  • Changelog split integrity: compared dev's [Unreleased] section against the PR's [3.7.0] + [3.6.0] sections — both contain exactly 390 bullet entries, confirming the split moved content byte-for-byte with nothing lost, duplicated, or reworded. Link reference footnotes ([#NNNN]: ...) are unchanged. Version headers are in correct descending chronological order (3.7.0 2026-09-10 → 3.6.0 2026-08-27 → 3.5.0 2026-08-19).
  • README tool counts: Darling's "139 tools" (added in two places, the comparison table and the MCP section) matches the figure already hardcoded in Darling/PerformanceMonitor.Darling.Service/Mcp/DarlingMcpInstructions.cs ("This server exposes 139 tools..."), so it's consistent with what the running service already self-reports rather than a new/unverifiable number.

One minor nit left as an inline comment: the split introduces a duplicate blank line right before the ## [3.5.0] header (line 428) that isn't present in dev or at the new [3.7.0]→[3.6.0] boundary this PR also creates — cosmetic only, easy one-line fix.

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
Comment thread CHANGELOG.md
@@ -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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@claude

claude Bot commented Sep 10, 2026

Copy link
Copy Markdown

Review summary

Reviewed 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, OPTION(RECOMPILE), etc.) don't apply here.

Checked and looks correct:

  • Directory.Build.props version bump 3.6.0 → 3.7.0 — single line, matches convention.
  • CHANGELOG split: ## [3.7.0]/## [3.6.0] headings present with ### Added/### Changed/### Fixed subsections in the new 3.6.0 block; reference-style link definitions ([#NNNN]: ...) still balance 1:1 against in-body [#NNNN] usages (1262/1262) — no dangling or orphaned references from the reshuffle.
  • Darling/Darling.Tests/MigrationUpgradeLadderLiveTests.cs: the TheMostRecentRelease_HasALadderFixture fix (deriving the expected fixture from the newest changelog heading that is not Directory.Build.props's current <Version>) is correct. Traced through the full cut/steady-state cycle — since Directory.Build.props only moves at a cut and stays pinned to the last-cut version for the whole following dev cycle, excluding it and taking the next heading down always resolves to the last tagged release, which is the only one a fixture can actually be generated against. This naturally forces the next cut's PR to add v3.7.0's fixture, same as this PR added v3.6.0's.
  • migration-ladder-v3.6.0.sql: tops out at V104 as described (103 rungs replayed), auto-generated header intact, no embedded secrets (the password/token hits are just column definitions for DPAPI-blob columns).
  • README table + MCP section tool counts (87 / 139 / 66) are consistent between the two edited spots.
  • No Lite/Darling parity concern — this migration-ladder guard is Darling/Postgres-specific infrastructure with no Lite (DuckDB) analogue to keep in sync.

One nit left as an inline comment: a leftover double blank line before ## [3.5.0] in CHANGELOG.md, inconsistent with the single blank line at every other version boundary in the file.

Nothing else stood out — this is a low-risk, mechanical release-prep PR.

@erikdarlingdata
erikdarlingdata merged commit 3de8902 into dev Sep 10, 2026
8 checks passed
@erikdarlingdata
erikdarlingdata deleted the release/v3.7.0 branch September 10, 2026 17:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant