Repository navigation
Lite's plan-cache trend descriptions still rated every point over the gap since the previous one, the shared unrated_note named one of two unrated reasons, and the latch/spinlock restart row was spelled two ways across SKUs: Lite carries #3700's three-state sentences, the note names the stored-0 restart beside the first-in-window LAG byte-identically on both SKUs, and both tools on both SKUs publish the unknowable row as null rates with interval_seconds beside them (partial #3653) - #3706
Merged
Merged
Conversation
…ated over the gap since the previous one, the shared unrated_note named one of the two reasons a point goes unrated, and the latch/spinlock restart row was spelled two ways across SKUs: Lite's descriptions carry #3700's three-state sentences, the note names the stored-0 restart beside the first-in-window LAG byte-identically on both SKUs, and both tools on both SKUs publish the unknowable row as null rates with interval_seconds beside them (partial #3653) Three clauses of #3653 A15/A16 plus the A11 residue, each named by a lane last night that could not touch the file. Lite/Mcp/McpQueryTools.cs get_query_duration_trend / get_procedure_duration_trend and the McpInstructions row said "a rate over the gap since the PREVIOUS point"; Lite's reads have been three-state since #3695 (query) and v61 (procedure). The descriptions now carry #3700's Darling sentences minus the hourly-route clause (Lite has one tier). get_query_store_duration_trend's description was still true (LAG-only) and is untouched. The trio's shared unrated_note said only "no previous one inside the window"; a restart collection (stored interval 0) has also been unrated on both plan-cache trends since #3695/#3700. One sentence now names both, byte-identical on both SKUs; McpMissMessageParityPinTests holds the literal, and the two substring pins (DarlingPerformanceTrendsReadTests, PerformanceTrendsToolTests) pin both reasons. Lite's get_latch_stats / get_spinlock_stats published a bare null delta on the restart row (#3702); Darling's published the raw 0 as the delta the band came from, interval_seconds: null, null rates, and a LOW severity banded from that 0. One vocabulary now: null rates + interval_seconds beside them (null when unknowable) + the latest delta null, on both tools and both SKUs. Lite gains interval_seconds and Darling's per-second pair per row; Darling's latch row gains row-level interval_seconds and nulls severity and severity_banded_from.delta_wait_time_ms on an unknowable interval; Darling's spinlock query gains the latch query's own latest_interval_seconds line so its null rates carry the why too. DarlingMcpLatchSpinlockTools.cs's class remark no longer says the delta collectors store no sample_interval_seconds (false since V127, #3595). McpPageContractTests.TheSameToolName_SpellsTheUnknowableRowTheSameWay_OnBothSkus holds the key sets equal from source; Lite's McpPageContractTests execute the three interval states over DuckDB; DarlingMcpLatchSpinlockToolsLivePostgresTests executes them over Postgres (stored 60 s rated 6000/60 not 6000/120, the LAG pair, the (0, 0) marker null on every key). Not done: the trend family's `truncated` (the #2364 window floor) is not renamed - the blast radius is ~15 consumers including get_query_store_top in DarlingMcpDataTools.cs, outside this lane. Reported.
Closed
39 tasks done
This was referenced Sep 19, 2026
erikdarlingdata
added a commit
that referenced
this pull request
Sep 20, 2026
… the page dialect's truncated: get_query_trend, the duration-trend trio and get_query_store_top publish the store's reach under its own key, every consumer and description follows, and the vocabulary census holds the two facts apart (partial #3653 — item 17) (#3793) `truncated` carried two facts on the wire. On twenty-odd paged tools it is the caller's `limit` biting — observed off a `cap + 1` fetch, beside `*_returned`, remedied by a bigger limit. On the #2364 / #2353 trend family and on get_query_store_top it was the store's REACH: the served series begins later than the requested start because the tier that answered no longer holds the window's head, beside `effective_start` / `effective_hours_back`, and no limit changes it. #3703 classified the homonym and left it as stated residue; #3706 counted the blast radius (~15) and stopped. This is that rename. Emitters (4): DarlingMcpTrendTools get_query_trend initializer + TrendDisclosure.WriteTo (the trio, data and empty envelopes); DarlingMcpDataTools get_query_store_top (truncation_note keeps its name — it is the prose for this flag); Lite McpQueryTools.WriteDisclosure (the trio + get_query_trend). Consumers: server-tabs.js drawQueryTrend reads window_truncated (undefined is falsy — the old key would have silently dropped the notice). Descriptions: McpHelpers.WindowTruncatedDescription, one shared clause on the nine publishing tools (5 Darling, 4 Lite), naming the key, the reach beside it, and the WIRE CHANGE; nine instructions rows and five web-catalog rows say window_truncated. C# members (Truncated, TruncationSlack, DescribeCoverage) keep their names — the wire key renamed. Census: McpPayloadContractCensusTests classifies window_truncated as the second bound it is (SecondBoundCutKeys), reads the ordered-envelope idiom (`envelope["key"] =`) the disclosure block uses so Lite's writer is in the roster, and adds TheWindowFloor_IsSpelledWindowTruncated_BesideItsReach_AndNeverBareTruncated_OnBothSkus: every block on both SKUs that writes effective_hours_back must write window_truncated and effective_start and never bare truncated — brace-walked over strings-blanked code, rostered as an equality on (file, idiom, count), checked first on a synthetic offender and a synthetic page cut. The discriminator is the reach vocabulary, not window_start: get_query_heatmap publishes window_start beside a bare truncated that IS a page cut. EveryWindowFloorTool_CarriesTheSharedClause_AndNoOtherToolDoes is the description half. The residue sentence is gone.
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.
Two Lite descriptions that #3695 made false, a shared sentence that named one of two reasons, and one restart row spelled two ways
Partial #3653 — three clauses of A15/A16 plus the A11 residue, each named last night by a lane that could not touch the file (#3700 named the first two, #3702 the third). Does not close the issue. A fourth clause (#3703's
truncated-carries-two-facts finding) was measured and deliberately NOT done; the count and the reason are at the end.1. Lite's plan-cache trend descriptions said every point was a rate over the gap since the previous one
Lite/Mcp/McpQueryTools.csget_query_duration_trendandget_procedure_duration_trend, and theget_query_duration_trendrow inLite/Mcp/McpInstructions.cs, still said "Every point is a rate over the gap since the PREVIOUS point, so the window's first collection … carries null rates". Lite's reads have been three-state since #3695 (GetQueryDurationTrendAsync) and v61 (GetProcedureDurationTrendAsync):MAX(sample_interval_seconds)per collection,NULLIF(…, 0)so a restart collection is UNRATED, the LAG only where the store never recorded an interval — verified at the source before editing (LocalDataService.QueryStats.cs:1178–1180,:1273–1275). A caller reading the old sentence would take a null on a mid-window point as a bug, when it is the restart the description never mentioned.Both descriptions now carry #3700's Darling sentences minus the hourly-route clause, plus one clause saying Lite has one tier.
get_query_store_duration_trend's description (LAG-only, no rollup on Lite) was still true and is untouched. Theget_procedure_duration_trendinstructions row gains six words pointing at the same rule.2.
unrated_notenamed one of the two reasons a point goes unratedThe trio's shared envelope sentence —
DarlingMcpTrendTools.SerializeTrendand LiteMcpQueryTools.SerializeTrend, byte-identical and pinned so byMcpMissMessageParityPinTests— said "a per-collection rate is the work since the PREVIOUS collection … and the window's first collection has no previous one inside the window". Since #3695/#3700 a stored-0 restart collection is ALSO unrated on both plan-cache trends, on both SKUs, and the note explained a null on that point with a reason that did not apply to it.One sentence now names both: the collection's STORED sample interval is 0 (a restart or counter reset: the collector could not difference its two snapshots, so the zeros beside it were never measured), or the point is rated against the PREVIOUS one and has none inside the window (the window's first collection where no interval was stored, or one landing in the same second as its predecessor). It stays true on the Query Store trend, which stores no interval and can only hit the second arm — every one of its points is "a collection where no interval was stored".
Unknowable is not 0is kept verbatim:McpZeroIsAMeasurementTests.TheTrendTools_CountAndExplainUnratedPointsanchors on it.Three pins moved deliberately: the literal in
McpMissMessageParityPinTests(60/60 executed — the fragment must appear in BOTH SKUs' trees, which is the byte-identity proof), and the two substring pins inDarlingPerformanceTrendsReadTestsandPerformanceTrendsToolTests, which now assert both fragments — the reason their seed exercises (first-in-window LAG) and the one it does not (stored 0) — so a rewording that drops either reds there and not only in the parity pin. The stale comment above Darling's note ("it still names only the first-collection case; widening it is a both-SKU edit") is replaced by the reason the sentence carries both arms.3. The latch/spinlock restart row was spelled two ways across SKUs
Read at the source, the two twins were never one shape: Darling's
get_latch_stats/get_spinlock_statsare WINDOW AGGREGATES (total_delta_*summed overhours_back, rates and the band from the LATEST interval), Lite's are the newest snapshot (cumulative counters plus the last interval's deltas). The class remark on Darling's tool file says so. What the brief's finding actually named is narrower and real: the row whose latest interval was unknowable — the (0, 0) restart marker beside a storedsample_interval_seconds = 0— was published asdelta_* = null, nothing beside it to say why;waits_per_second/wait_ms_per_secondnull,severity_banded_from.interval_secondsnull, butseverity_banded_from.delta_wait_time_ms = 0(the marker's fabricated 0, published as the delta the band came from) andseverity = "LOW"— banded from that 0 byLatchSeverity(0);collisions_per_second/spins_per_secondnull, and nothing beside them at all — the reader never returned the interval for spinlocks.One vocabulary, argued rather than picked. The brief preferred Darling's shape (
interval_secondsbeside the delta — it says WHY the rates are null). Half of that is right and adopted:interval_secondsis the why-key, and Lite gains it, with Darling's per-second pair beside it. The other half of Darling's shape — the raw 0 published as the delta — is the #3642 lie both viewers' snapshot grids stopped rendering last night (#3702: "—" with "restart / first sample" beside it) and Lite's tool stopped publishing in the same PR; adopting it would revert that a night later and put the MCP payload at odds with the grid on the same SKU. So the row is spelled, on both tools and both SKUs: rates null,interval_secondsnull beside them, the latest delta null — the delta says the numbers were never measured, the interval says why, and neither is inferred from the other. Concretely:get_latch_statsrows gainwaits_per_second,wait_ms_per_second,interval_seconds;get_spinlock_statsrows gaincollisions_per_second,spins_per_second,interval_seconds. The rate is the row's delta over its stored interval (PerSecond), rounded as Darling rounds;interval_secondsis the stored seconds when > 0 and null otherwise (KnownInterval). A pre-v60 row that never stored an interval keeps its deltas (they are real) and nulls only the interval and the rates — one snapshot row has nothing to LAG against, and the rate is not invented. The deltas are unchanged from The perfmon chart plotted deltas under a "Value" label with the divisor sitting unused on the row, and a latch/spinlock restart read as zero: both viewers shape every perfmon series through the stored interval (rate counters per second by the /sec name proxy, the rest labelled per interval, the unknowable point a line break) and both snapshot grids render the (0, 0) marker as unknowable with the interval beside it (partial #3653) #3702.get_latch_statsrows gain a row-levelinterval_secondsbeside the two rates (restated insideseverity_banded_fromthe way that block already restatescaptured_atbesidelatest_collection_time— the block is the band's provenance in one place, this is the rates' denominator in its place);severityandseverity_banded_from.delta_wait_time_msare null wheninterval_secondsis — no band from a number nobody measured.get_spinlock_statsrows gaininterval_seconds, which needed the reader:SpinlockStatsTopNSqlnow carries the latch query's ownlatest_interval_secondsline (same CTE, sameCASE WHEN interval_seconds > 0expression, one more select column),SpinlockStatRowgains the field. That is a SQL const change and was executed on a rig (below). The reader file was fenced "(doc)"; this is the load-bearing twin port the pin needs and is stated as such.DarlingMcpLatchSpinlockTools.cs's class remark no longer says "the per-second rate is derived in SQL from the per-class LAG interval, because Darling's delta collectors store nosample_interval_seconds" — false since V127 (The four naked delta families store the interval their deltas accrued over, so a restart's fabricated zero reads as unknowable instead of 0.00 ms/sec (#3540, V127 / Lite v60) #3595). It says the stored interval is the source and the LAG the pre-V127 fallback, and carries the one-vocabulary paragraph.Why not
sample_interval_secondsfor the key. Six sites on the wire (get_file_io_stats,get_perfmon_trend,get_memory_grants, both SKUs) publish the stored interval under the column's name; two (Darling's latch block, the PG trends) sayinterval_seconds. The latch/spinlock tools already spelled itinterval_secondson Darling and this PR makes the pair agree with itself across SKUs; collapsing the two spellings across the whole surface is a vocabulary pass of its own (residue, noted for the coordinator).What
severity: nullchanges for a reader.server-tabs.js'sLATCH_COLUMNSrendersseveritythroughstatusSev→statusToSev(String(status || ""))→ the default arm, andapplyFormatprints—for a null — no JS edit needed and none made.DarlingMcpInstructions.cs's latch row ("severityis banded from the LATEST interval only") stays true and is untouched.The pins
Darling.Tests/McpPageContractTests.TheSameToolName_SpellsTheUnknowableRowTheSameWay_OnBothSkus(new) — theTheSameToolName_EmitsTheSamePageContractKeys_OnBothSkusidiom for the two tools whose rows are not the same shape: per tool, the set ofinterval_seconds/*_per_secondkeys emitted by each SKU's projection is exactly the expected three, and each description on each SKU namesinterval_secondsand says the row isunknowable. Matcher witnessed both ways (avg_wait_ms_per_request,spins_per_collisionare ratios, not rates, and do not match).Lite.Tests/McpPageContractTests.GetLatchStats_PublishesNullDeltas_OnTheRestartMarkerRow(extended) andGetSpinlockStats_PublishesNullDeltasAndRates_OnTheRestartMarkerRow(new) — real DuckDB through the real tools, three interval states per tool: the marker null on every key that spells it; the measured row rated over its STORED 60 s (4,000 ms / 100 requests → 66.67 ms/s, 1.67 waits/s; 4,000 collisions / 1,000 spins → 66.67, 16.67 per second); the pre-v60 row keeping its deltas with the interval and rates null.DarlingMcpLatchSpinlockToolsLivePostgresTests(extended) — the same three states over Postgres through the real tools: a class whose newer row stores 60 s while the pair is spaced 120 s apart (6,000 / 60 = 100 ms/s proves the stored path; 50 would have proved the LAG ran where it should not), a pre-V127 pair rated by the 120 s LAG (16.67), and the marker:interval_seconds, both rates,severity,severity_banded_from.delta_wait_time_msand.interval_secondsall null,total_delta_wait_time_mshonestly 0, the row still on the page. Spinlocks: 60,000 / 60 = 1,000 and 300,000 / 60 = 5,000 per second on the stored row, three nulls on the marker.DarlingMcpLatchSpinlockToolsSurfaceAndSqlTests— both SQL pins gainEND AS latest_interval_seconds/l.latest_interval_seconds; newTheUnknowableRow_IsNullOnEveryKeyThatSpellsIt_InBothProjectionspins the projection shape from source (the null predicates present, the retired spellings absent, the false class remark gone).McpLatestSnapshotStampTests.LatchSeverity_NamesTheIntervalItWasBandedFrom— its anchor ondelta_wait_time_ms = r.LatestDeltaWaitTimeMsmoved to the guarded spelling, on purpose.What this PR does NOT do (clause 4, measured)
truncatedon the trend family (get_query_duration_trend/get_procedure_duration_trend/get_query_store_duration_trend/get_query_trend, both SKUs) and onget_query_store_topmeans the #2364 WINDOW floor, not #3594's page cut. The brief said rename it (window_truncated) if the blast radius stayed inside ~6 consumers, else stop and count. Counted: ~15. Emitters and descriptions:DarlingMcpTrendTools.cs(TrendDisclosure.WriteTo,get_query_trend's anonymous payload, three doc paragraphs), LiteMcpQueryTools.WriteDisclosure, fourMcpInstructions.csrows, fourDarlingMcpInstructions.csrows, andget_query_store_top— which lives inDarlingMcpDataTools.cs:765–848, not in the trend tools file the brief named, and outside this lane's fence. Consumers:server-tabs.js:379(drawQueryTrendreadstrend.data.truncated). Tests:PerformanceTrendsToolTests(4 asserts),DarlingPerformanceTrendsReadTests(4 asserts + the six-key order block),TrendEmptyParityToolTests(4),QueryStoreTrendRoutingLiveTests(3, live),McpZeroIsAMeasurementTests:784,QueryStoreTopWindowTests:123,DarlingMcpTrendToolsTests(prose), andMcpPayloadContractCensusTests'CutNoteKeysroster + class residue paragraph. Lite's envelope DOES publish the key (the brief's hypothesis that it might not was wrong —WriteDisclosuresets it offTruncationSlack), so the rename is both-SKU. Stopped, per the brief; ships 1–3.Also not done: the frozen
deprecated/Dashboardtwins (a semantics upgrade, not a bug fix, on a frozen twin);sample_interval_secondsvsinterval_secondsacross the six other wire sites;CHANGELOG.md; anyPgTarget*, collector, perfmon,MeasureCatalogorDarlingTrendReaderfile.Executed here
Darling.Tests built
net10.0-windows, run in-process with the WindowsDesktop framework entry stripped; the live classes against a throwawaytimescale/timescaledb:2.28.1-pg18(Docker contextdesktop-linux, migrated byPgMigrations, torn down):DarlingMcpLatchSpinlockToolsSurfaceAndSqlTests24/24,DarlingMcpLatchSpinlockToolsLivePostgresTests1/1 (live),DarlingPerformanceTrendsReadTests1/1 (live),McpPageContractTests43/43,McpLatestSnapshotStampTests34/34,McpZeroIsAMeasurementTests20/20,McpPayloadContractCensusTests49/49,DarlingMcpTrendToolsSurfaceAndSqlTests43/43,DarlingMcpToolsTests16 (1 pre-existing skip),AsOfWindowAnchorTests29,RepoFileAdoptionTests2,CommentFilterAdoptionTests4,ServerPageTabsTests17,FleetIdentifierScrubTests2,DocCommentHygieneTests77 — green. Lite through a throwawaynet10.0-windowsharness Compile-Including the classes:McpMissMessageParityPinTests60/60,PerformanceTrendsToolTests5/5,TrendEmptyParityToolTests5/5,McpLatestSnapshotStampTests12/12,McpDescriptionTruthPinTests4/4,McpPageContractTests19/21 andDeltaFamilyUnknowableRowReadTests11/12 — the three reds are the knownIgnoredWaitTypes.Load()→PresentationFrameworkplatform artefact on untouched wait-stats reads, reproduced on dev by #3702/#3703.Builds
-c Release -p:EnableWindowsTargeting=true:PerformanceMonitor.Darling.Service,PerformanceMonitorLite,Darling.Tests— 0 warnings;Lite.Tests— dev's one pre-existing xUnit2000 atLogTailOverlapThresholdPinTests.cs:95, untouched. No JS edited (node --checknot applicable); zero hits for the retired spellings (no previous one inside the window,per-collection rate is the work since) across the tree.First executed in CI: the rest of Lite.Tests. No SQL const changed except the spinlock reader's one added column, executed above.
Refs #3653 (partial — A15/A16 Lite trend descriptions +
unrated_note+ latch/spinlock unknowable-row parity; A11's Lite description residue). Review-dark window: auto-merge NOT armed; self-review in the lane report.Which component(s) does this affect?
How was this tested?
See Executed here: in-process on this Mac, the two live classes against a throwaway PostgreSQL 18 + TimescaleDB 2.28.1 store; no SQL Server involved (store-side reads and descriptions only).
Checklist
dotnet build -c Release -p:EnableWindowsTargeting=true)