Status: a work queue, being worked easiest-first. Nothing here waits on a ruling or a prerequisite — no ruling from the maintainer is pending, no prerequisite PR is open, and every item below is independent of the others. Each item is a lane of its own (S/M for the mechanical ones, multi-day for the four marked structural). It stays open until its checklist is empty; items marked structural carry a one-line reason for staying and are the last to go.
The 2026-09 brains-review campaign (five adversarial lanes graded against the OtterTune corpus; wave 1 #3523 –#3563 , wave 2 #3538 –#3541 + #3526 /#3540 keystone) closed its four lane issues on 2026-09-18 with 21 wave-2 PRs merged in one day. This issue holds what was deliberately deferred — every item below is structural or multi-day by the reviews' own sizing, or is a frozen-twin mirror batched here so it lands as one faithful diff. Nothing here is forgotten; each line names its evidence and its shape. Measured inputs (sanitized) are in the wave-2 measurement record on the design bus (pm-issue-queue-inbox/design/), filed beside the PG-analysis v1 plan.
Analysis engine (from #3538 )
A8 Peak-vs-per-sample baseline bias — detectors test the window MAX against a per-sample hour×dow distribution, so the null expectation of the statistic rises with N; a 24 h as_of pass mechanically reports more anomalies than the 4 h scheduled pass on identical behaviour, and the IO detector uses AVG while its siblings use MAX under shared cutoffs (PgAnomalyDetector.cs, AnomalyThresholds.cs:22). Shape: baseline the statistic you test (per-window peaks per bucket), or gate on peak AND window mean, or an N-aware threshold term. Structural; needs a 30-day planted series to validate. (Q1 first slice in PR The anomaly gate judges the window peak AND the window mean, so one hot sample stops reading as an anomaly and a longer window stops buying more of them; I/O reads the pair like its siblings (Partial #3653 — item 1, Q1) #3724 : every detector gates on window peak AND mean under the existing cutoffs; IO on the same pair; the per-window-peak-per-bucket family waits for the ~day-5 re-read)
A10 Config → outcome attribution — server_config_changes / database_config_changes / trace_flag_changes have zero references in either analysis project; ForcePlanSelfReview proves the verify-after pattern in-repo. Shape (engine review C2): on a new change row in the worker's AN3 block, run ComparePeriodsAsync (now dispersion-banded, compare_analysis bands each delta by the server's own dispersion and folds one cause into one row, so same-hour-yesterday noise stops reading as a verdict (#3538 A3) #3634 ) around the change timestamp and emit a CONFIG_CHANGED finding with sigma-scaled deltas. The single biggest credibility upgrade available; medium risk; its own design pass. (Q2 delivered in PR A server configuration change now has a consequence the engine states: CONFIG_CHANGED compares the ±4 h around it and says what moved, or that nothing did (Partial #3653 — item 2, Q2) #3720 — server config slice: one CONFIG_CHANGED Information finding per change event on both SKUs, ±4 h ComparePeriodsAsync banded by ComparisonBanding, prose says "first observed" and discloses the partial after half; there is NO server_config_changes table — changes are ConfigChangeDiff over the on-connect server_config snapshots; database config + trace flags: SLICE TWO DELIVERED in PR CONFIG_CHANGED spoke only for sp_configure: a database option or a trace flag changing inside the window was a history row and no finding. The same fact now covers all three snapshot families, and changes the connect observed together are one card (Partial #3653 — item 2, A10 slice two) #3786 (12e7d2e85, 2026-09-20) — the same CONFIG_CHANGED fact now covers all three snapshot families, with changes observed together at one connect rendered as one card; item complete, both families shipped)
A9 structural half — recurrence-aware scoring (an occurrence streak at the same hour×dow discounts or labels "recurring at this hour") and detection of a maintenance window that MOVES; the mechanical edges landed in Story confidence measures corroboration instead of path length, and the nightly rebuild's three cards become one incident that names the job (#3538 A6/A9) #3632 . Also: RUNNING_JOBS carries no job name (Pg/DuckDbFactCollector.Activity.cs emit doubles only; Fact.ObjectName could carry it — collector change both SKUs). (the RUNNING_JOBS job-name clause landed in PR The analysis said an Agent job was running long and could not say which: the RUNNING_JOBS fact now names the job furthest past its own history on both SKUs, and the card, the frozen finding text and the headline say which one (partial #3653) #3693 ; recurrence/moving-window await ruling Q3) (recurrence label + moving-window label in PR A chain that fires every Tuesday at the same hour was rated a fresh incident each week and nobody was told: the pass now labels it "recurring at this hour" at unchanged severity, and a weekly Agent job whose slot slid gets "maintenance window moved" (partial #3653 item 3, Q3) #3725 — Q3 ruled LABEL at unchanged severity)
(PR The analysis pass said "collection appears to have stopped or broken … NOT an all-clear" for a window the coverage witness proves the collector observed: the empty-window rule tested facts.Count == 0 before it tested the witness, so an observed window that simply produced no fact wore the dead-collector envelope — unavailable with a pointer at collection health, the Viewer's dead-collector marker. The rule now gates on the witness alone, and an observed window with no facts runs the pass and reads empty at its stated coverage, both SKUs (#3653) #3687 — the dead-collector envelope gates on ObservedDurationMs <= 0 alone, both arms byte-identical; an observed window with no facts runs the pass and reads empty at its stated coverage, WindowEmptyMessage null so the worker clears its marker; one Information line names the coverage for the scheduled pass; cross-SKU parity pin in Darling.Tests) Empty-window rule wording, both SKUs — DarlingAnalysisService.cs / Lite/Analysis/AnalysisService.cs: facts.Count == 0 || ObservedDurationMs <= 0 says "collection appears to have stopped" even when the coverage witness proves the window WAS observed and simply emitted no fact (now that every collector stamps coverage, Analysis facts divide by the time the collector actually observed, and a window with a hole in it says so, so collector downtime no longer reads as improvement (#3538 A2) #3592 /PostgreSQL targets enter the analysis pipeline: the pass routes by registry engine_kind inside the service, a pg_database_stats coverage witness replaces the tombstone, and every shared switch gains its one pg_ arm so the content lanes can build in parallel (#3542 v1 plumbing) #3665 ). Shape: gate on ObservedDurationMs <= 0 alone; keep the "point-in-time facts only" prose for the observed-zero case. Twin edit, S.
BLOCKING_EVENTS scorer pair (10, 50) vs the alerting layer's measured (5, 20) — not one instrument (the story reaches CRITICAL via amplifiers); decide on its own evidence. (DECIDED 2026-09-22 from the 14-day fleet read: different UNITS — the scorer averages over the pass window, the alerting pair is per hour; 4-h p99 = 59/h so (10,50) is right for its grain and stays with lineage; the 24-h as_of shape structurally cannot reach 50 → BLOCKING_EVENTS grades a window average, so the same storm is CRITICAL on a 4-hour pass and Information on a 24-hour one — grade the busiest 4-hour sub-window instead #3871 grades the busiest 4-h sub-window instead.)
Wait-profile ratio ramp / DefaultRatioThreshold uncalibrated (MEASURED 2026-09-22: 3,655 windows, ratio p99 = 3.14, p99.9 = 28.3, ≥ 4.0 on 0.63 % — 4.0 stays, lineage paragraph + threshold_lineage = 1 ride the next lane through AnomalyThresholds.cs; scheduler-count clause RESOLVED 2026-09-22: the column is cpu_count (4–16 cores, median 8 on the primary fleet); per-scheduler normalisation CANCELS out of the ratio — numerator and its own-baseline denominator divide by the same constant — so the ratio bar is unchanged; the one place core count is real is the ABSOLUTE magnitude floor (500 ms/s means 4× different saturation on 4 vs 16 cores) — a design note, not a recalibration; whether that floor becomes per-core rides the next lane in AnomalyThresholds.cs as a doc paragraph) — one more column on the fleet wait pass (the ratio's own per-server-window distribution). Scheduler-count normalisation of the wait fraction (same fraction means different things on 4 vs 64 cores) — documented on the table; needs its own read.
(PR Six small honesty riders from the brains-review residue: a retired knob says so on the wire, get_pg_server_config says when it was captured, Lite's latch/spinlock pages bind to the caller's limit, the ORDER-BY-integer fold gets a census, and two rung docs stop asserting superseded facts (#3653) #3659 — Lite; the Darling twin in DarlingMcpTools.cs is a one-line follow-up owned by the PostgreSQL-target analysis engine: design and v1 build #3542 lane's coordinator, the Dashboard twin belongs to the frozen-twin batch below) ToolRecommendations has no HADR_SYNC_COMMIT entry (Lite/Darling/Dashboard) since The scorer's wait thresholds carry their measurement: WRITELOG stops saturating on a third of routine windows, deadlocks grade 5/hr apart from 90/hr, and the absolute gates scale with the hours actually observed (#3538 A5/A7) #3616 gave it a scorer entry; drill-down collectors attach nothing for it.
Alert semantics (from #3539 )
A5 Persistence-gate rollout — measured on one production store class (43 servers), three conditions need THREE different honest shapes, not one gate: tempdb Space → consecutive-sample gate K = 3 (one server flapped ≥80% 22× in 14 d, every run 1–4 samples ≤206 s; K = 3 keeps 2 of 22); Blocking Wait Time → duration-OR-consecutive disjunction (102 episodes, 97 single-snapshot at ~72 s cadence including the p99 1,509 s / 145-session event — a consecutive gate would drop exactly the episodes that matter; the twin fleet's ~1 h-quantized arcs DO run consecutive); Long-Running Query → opt-out coverage, not a gate (191 distinct ≥30-min sessions in 7 d, p90 seen in 6,192 snapshots — permanent background requests; the "what are they" program_name/wait_type breakdown is the follow-up read). (tempdb Space K = 3 delivered in PR tempdb Space fired on one collected sample and resolved on the next: the arm now holds for 3 consecutive collections behind the shared persistence gate, counted per collection rather than per sweep, with no-data freezing it instead of resolving to N/A (#3653 A5, tempdb slice) #3692 ; Q4 in PR Blocking Wait Time fires on one snapshot at 3× the bar or on 3 consecutive collections, not on every snapshot; Long-Running Query gets an opt-out knob seeded from the production read — job-step program prefix, the two NT AUTHORITY logins — applied ahead of the row cap and counted by arm (#3653 A5, Q4 + Q5) #3734 : single snapshot at 3× fires at once, else 3 consecutive collections through the shared gate; Q5 opt-out knob seeded from the production read — job-step program prefix + the two NT AUTHORITY logins — in PR Blocking Wait Time fires on one snapshot at 3× the bar or on 3 consecutive collections, not on every snapshot; Long-Running Query gets an opt-out knob seeded from the production read — job-step program prefix, the two NT AUTHORITY logins — applied ahead of the row cap and counted by arm (#3653 A5, Q4 + Q5) #3734 , Darling store home + MCP twins follow as V135 in PR The Long-Running Query opt-out knob gets its Darling store home: V135 adds the two lists to config_alert_settings with the production read's seeds as their DEFAULT, wired through the service, the Viewer, the probe and both SKUs' get_alert_settings / update_alert_settings twins (#3653 A5, Q5) #3736 , merged dc1ddeb ; the "what are they" read landed and seeded the defaults — ticked by the coordinator) Q5's PostgreSQL twin: The PostgreSQL Long-Running Query twin ignores the program/login opt-out knob: the setting is visible, editable and read back, and does nothing on a PostgreSQL target #3743 → PR The PostgreSQL Long-Running Query twin honours the program/login opt-out knob, so a setting an operator can see, edit and read back on a PostgreSQL target stops doing nothing there — shared predicate builder over application_name/usename, ahead of the row cap, counts by arm on the card (#3743, partial #3653) #3770 (the PG LRQ read honours the same knob through the shared predicate builder, ahead of the row cap).
A8e engine half (The PostgreSQL poison-wait host holds on silence like the SQL Server engine does, and three presence-flat alerts grade their severity from the bars the health bands already measured (#3653 alert riders) #3660 ) — Deadlocks Detected / High CPU / tempdb Space fire with Severity: null (AlertEngine.cs:650, 1135, 1489); the grids now render whatever the engine grades (A server nothing has banded yet is Unknown, not Healthy, and the alert-history grids show the severity the alert actually fired at instead of the colour its name implies (#3539 A6/A8e) #3635 ), so grading is engine work. A8d's 20% collector Critical bar is by analogy, not measured.
(PR get_pg_server_config counted the page and called it the server: non_default_count is now the snapshot's, computed above the cap, truncated is observed off a limit + 1 fetch, and its three sibling pages stop inferring truncation from a full page (#3653 A3 residue) #3679 — non_default_count is the snapshot's via COUNT(*) FILTER … OVER () above the LIMIT, non_default_returned is the page's, include_defaults moved into the statement, truncated observed off limit + 1; the three sibling pages in the same file — extensions, lock stats, config changes — observe too. Three MORE >= limit sites remain in OTHER files: DarlingMcpPgDeadlockTools.cs:82, DarlingMcpPgPlanTools.cs:210 (BuildReadinessJson), DarlingMcpPgIndexTools.cs:150, each against its own pins) (the three remaining >= limit sites fixed in PR Three PostgreSQL pages observe truncation off limit + 1 through one shared helper, the census sweeps the inference class instead of a roster, and four payload rules become facts and inventories, so three tools stop guessing truncation from a full page and four of the eleven payload rules stop being sentences (partial #3653) #3699 , plus get_pg_plans' unannounced Take(limit) beside them; all four through McpHelpers.BoundPage) get_pg_server_config A3 residue — DarlingMcpPgServerStateTools.cs:~640: truncated = rows.Count >= limit (inferred, the MCP pages now say what bounded them: caps bind to the caller's limit, truncation is detected not inferred, and no page count is called a total (#3541 A3) #3594 class) and non_default_count is page-scoped (counted over fetched rows, not the snapshot). Darling-only tool. S. (Correction from Six small honesty riders from the brains-review residue: a retired knob says so on the wire, get_pg_server_config says when it was captured, Lite's latch/spinlock pages bind to the caller's limit, the ORDER-BY-integer fold gets a census, and two rung docs stop asserting superseded facts (#3653) #3659 : PG config is collected HOURLY — CollectorScheduleDefaults["pg_server_config"] = (60, 365) — not on connect; the connect-time shape is SQL Server's family.)
(PR The four PostgreSQL host alerts fire with the tier they earned instead of Severity: null, so their history rows stop wearing the colour their name implies (#3653 A8e, PostgreSQL half) #3680 — CPU on ServerHealthClassifier.CpuSeverity, deadlocks on GradeDeadlockFire + the store's DeadlockRateThresholds, blocking/LRQ explicit Warning-only with the missing bar named) PG host arms still fire Severity: null — DarlingWorker.EvaluatePgDeadlocksAsync, EvaluatePgBlockingAsync, EvaluatePgLongRunningQueryAsync (:4600) and EvaluatePgCpuAsync (:4057): the A8e class The PostgreSQL poison-wait host holds on silence like the SQL Server engine does, and three presence-flat alerts grade their severity from the bars the health bands already measured (#3653 alert riders) #3660 fixed on the SQL Server engine. PG CPU already computes the ACU capacity percent (FleetCpuProvenance.CpuBandInputPercent) so ServerHealthClassifier.CpuSeverity grades it directly; PG deadlocks can reuse GradeDeadlockFire with DeadlockRateThresholds. READMEs say "not yet graded". S.
PG blocking band stays Unknown-with-reason (sampled evidence); Ingest target PostgreSQL logs as telemetry — errors, spills, autovacuum runs, and connection churn are invisible between counter samples #3601 's lock_wait log events are the event-grain source a report-rate band would read. (READ SPEC on the bus 2026-09-22 09:05Z — pg_log_events lock_wait reports per cluster-hour since .450 + log_lock_waits coverage; decision rule stated there: adopt the SQL Server tiers by reference if the shape is bimodal and ≥ 80 % of clusters log; unobservable for clusters with logging off; defer to the 14-day mark if < ~500 logged cluster-hours. DECIDED 2026-09-22 09:00Z: log_lock_waits is OFF on 50/50 clusters; zero lock_wait rows ever (parser fine — 388 K error-family rows prove the pipe); sampled edges show blocking DOES occur (p99 14 captures/cluster-hour) — a real observability gap. Band stays Unknown fleet-wide by design; PostgreSQL blocking family: an operator now learns who headed a blocking chain, in what state, and how long the sessions behind it had really waited — PG_BLOCKING_CHAIN from the sampled edges, PG_LOCK_WAIT_EVENTS from the engine's own log lines, PG_LONG_RUNNING_QUERY from the active sessions, and a blocked-sessions anomaly whose baseline counts a quiet capture as zero (partial #3691, lane 17) #3750 's PG_LOCK_WAIT_EVENTS already says log_lock_waits is off rather than 0 and PgTargetFactCollector.Blocking.cs:334 reads the setting, so the product side is complete. The lever is infra: one RDS parameter-group change (negligible overhead — logs only waits crossing deadlock_timeout), after which the 14-day calibration clock starts and the SQL Server tiers are adopted by reference if the shape is bimodal. Not a lane.)
PG poison-wait host clears on an EMPTY read (The PostgreSQL poison-wait host holds on silence like the SQL Server engine does, and three presence-flat alerts grade their severity from the bars the health bands already measured (#3653 alert riders) #3660 ) (DarlingWorker.EvaluatePgPoisonWaitAsync) while the SQL engine holds since SQL Server's Poison Wait alert measures accumulated starvation over a ten-minute window like its PostgreSQL twin, so one slow wait no longer pages and a THREADPOOL storm no longer sleeps (#3539 A4) #3593 (unwatched ≠ quiet) — adopt the observed-window clear.
(PR Six small honesty riders from the brains-review residue: a retired knob says so on the wire, get_pg_server_config says when it was captured, Lite's latch/spinlock pages bind to the caller's limit, the ORDER-BY-integer fold gets a census, and two rung docs stop asserting superseded facts (#3653) #3659 ) get_alert_settings.poison_wait.threshold_ms is reported and accepted but unconsulted since SQL Server's Poison Wait alert measures accumulated starvation over a ten-minute window like its PostgreSQL twin, so one slow wait no longer pages and a THREADPOOL storm no longer sleeps (#3539 A4) #3593 — deprecation note on the wire.
(PR Six small honesty riders from the brains-review residue: a retired knob says so on the wire, get_pg_server_config says when it was captured, Lite's latch/spinlock pages bind to the caller's limit, the ORDER-BY-integer fold gets a census, and two rung docs stop asserting superseded facts (#3653) #3659 ) PgMigrations.cs V79 / V122 rung docs carry pre-Alert-condition semantics campaign: window-invariance, persistence gates, engine parity (8 findings + parity table) #3539 statements ("grew N MB inside the window"; "a PostgreSQL server has no deadlock band") — historical rung rationale; one "(ended by #…)" clause each, next time the file is edited.
Measurement layer (from #3540 )
A5 Identity-epoch detection — sqlserver_start_time collected but consumed only for display; AG failover clears alert state but never delta baselines; pg_stat_statements_reset() invisible (pg_stat_statements_info.stats_reset has zero references). Shape: per-server epoch check (start_time / stats_reset / server_name) → ClearServer + a discontinuity marker the read layer renders. Also DarlingWorker.ReconcileServers "definition changed" branch reconnects under the same server_id without clearing baselines. The only genuinely structural measurement item. (mechanical slice — epoch comparator, persisted prior, ClearServer/ClearGroups before the subtraction, collection_log marker, the ReconcileServers forget — in PR Identity epochs: a target that restarted, failed over or was re-pointed no longer keeps subtracting from the old instance's counters — detection, forget and a logged, stored discontinuity on both hosts (partial #3653 A5) #3694 , merged 3386f2b ; carrier-order and Aurora-wait residues closed in PR Five families subtracted from the old instance once before anyone noticed it had changed; Aurora's wait counters had no one watching for a restart at all: wait_stats carries the SQL Server identity first in the order, pg_wait_stats carries pg_postmaster_start_time(), and both census rosters are empty (partial #3653) #3705 : wait_stats carries the SQL Server pair first in both hosts' order, pg_wait_stats carries pg_postmaster_start_time(), both census rosters empty. Remaining clause: the rendered marker.) (rendered marker: discontinuities[] on every trend payload + viewer/web notice, both SKUs, PR The store marked every identity epoch and no surface rendered it: every trend payload on both SKUs now ends with discontinuities[], the two desktop viewers draw the marker, and the web trend panels say the sentence (partial #3653 A5) #3787 )
A6 CAGG follow-up — (NULL-not-0 clause DELIVERED in PR The Daily-routed calendar prints NULL, not 0, for a day the rollup tier skipped below its ceiling and names it in days_missing[] — the hole scan's two probes per server at day grain, on both daily tools, the viewer's day-detail line and the web calendar (partial #3653 — item 9, A6) #3788 , 8ff53f4 , 2026-09-20: the Daily-routed calendar prints NULL for a day the rollup tier skipped below its ceiling and names it in days_missing; the >24 h hole repair delivered in Three hourly rollups counted every restart's zero as a sample and a >24 h outage left a two-hour hole under a floor that said covered: interval-honest successors beside the legacy trio with the phase grid re-derived by its own method, and a targeted refresh of each materialization hole at service start (partial #3653 — items 16 + 9, Q12 + Q10) #3731 . What remains is ONLY the legacy hourly trio's interval-honest successor, which needs three new aggregates on a phase grid that has 4 s of margin against its 896 s ceiling — grid-coupled, and Daily band re-derivation is DEFERRED until a daily successor is added (band full 23/23) — the slicing arm landed in #3865 (buckets_per_batch = 1); the storm class was fixed by the WAL ceiling (#3802) #3745 closed dormant, so this waits on grid capacity, not on a decision.) — TimescaleSupport.CreateWaitStatsBaselineSql still sums the restart zero and counts it as a sample; a CAGG cannot be altered in place and a rebuild forfeits ~35 days of baseline history. Shape: new aggregate under a new name with WHERE sample_interval_seconds IS DISTINCT FROM 0, WITH NO DATA + --backfill-rollups, retire the old via RetiredBaselineRelations (Retire the orphaned cpu_utilization_baseline / file_io_baseline continuous aggregates (#1995 cleanup) #2007 ). Plus the >24 h host-outage permanent unmaterialized hourly hole (RetentionTierRouter self-discloses) and the daily-tier calendar's unique_queries = 0 beside fresh raw numbers. (PR The wait and perfmon baselines stop counting every restart's fabricated zero as a quiet sample: interval-honest successor aggregates replace the legacy pair in place, the provider reads whichever supply covers its window and gates the LAG heuristic on a NULL interval, the legacy pair retires by a self-executing coverage condition, the startup backfill reads coverage off the materialization, and the Daily-routed calendar answers its unmaterialized days from raw (partial #3653: A6 + A10 mechanical half) #3698 — landed: perfmon_interval_baseline / wait_stats_interval_baseline REPLACE the legacy pair in BaselineAggregates (an append moves the phase grid: watch line 1,050→950 s vs the 896 s ceiling), the legacy pair retires through SupersededBaselineRelations when the successor covers the tier (~day 5, judged every start), the startup backfill now fires on the first start (its gate read coverage through the real-time view — -infinity watermark — and skipped), and the Daily-routed calendar answers the days past the rollup's ceiling from raw; remains: the >24 h hole (read honestly into RetentionTierRouter — it is the ≤2 h pre-outage tail, not the outage; repair delivered in PR Three hourly rollups counted every restart's zero as a sample and a >24 h outage left a two-hour hole under a floor that said covered: interval-honest successors beside the legacy trio with the phase grid re-derived by its own method, and a targeted refresh of each materialization hole at service start (partial #3653 — items 16 + 9, Q12 + Q10) #3731 as RepairMaterializationHolesAsync), and NULL-not-0 for a day skipped BELOW the rollup ceiling (payload-shape, census lane).) (Q10 delivered in PR Three hourly rollups counted every restart's zero as a sample and a >24 h outage left a two-hour hole under a floor that said covered: interval-honest successors beside the legacy trio with the phase grid re-derived by its own method, and a targeted refresh of each materialization hole at service start (partial #3653 — items 16 + 9, Q12 + Q10) #3731 : RepairMaterializationHolesAsync at service start — for every continuous aggregate, the bucket ranges inside its materialized span that the source holds rows for and it never materialized are each refreshed over exactly their bounds, plain first and forced only on a measured remainder, capped at one policy window per aggregate per start; no start_offset widened. The NULL-not-0 below-the-ceiling clause stays open, so the box stays open.) Q10 ruled → RepairMaterializationHolesAsync DELIVERED in PR Three hourly rollups counted every restart's zero as a sample and a >24 h outage left a two-hour hole under a floor that said covered: interval-honest successors beside the legacy trio with the phase grid re-derived by its own method, and a targeted refresh of each materialization hole at service start (partial #3653 — items 16 + 9, Q12 + Q10) #3731 (targeted refresh of >24 h holes at service start); remaining on this item: the daily-tier calendar's unique_queries = 0 beside fresh raw numbers, and the daily successors (follow-on issue filed today — the daily band must be re-derived first). (NULL-not-0 below the ceiling delivered in PR The Daily-routed calendar prints NULL, not 0, for a day the rollup tier skipped below its ceiling and names it in days_missing[] — the hole scan's two probes per server at day grain, on both daily tools, the viewer's day-detail line and the web calendar (partial #3653 — item 9, A6) #3788 : unique_queries is NULL and days_missing[] names the day; remaining: the daily successors — Daily band re-derivation is DEFERRED until a daily successor is added (band full 23/23) — the slicing arm landed in #3865 (buckets_per_batch = 1); the storm class was fixed by the WAL ceiling (#3802) #3745 )
A7 Viewer rendering — perfmon chart plots raw per-interval deltas ("Value", divisor fetched but unplotted); cntr_type not stored, so gauges are delta'd as activity (a rung); line charts interpolate straight across gaps (only the heatmap NaN-breaks); latch/spinlock snapshot grids show a (0,0) restart row as "delta 0". (PR 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 — three clauses landed; cntr_type rung V132 / v62 landed in PR A falling gauge read as a counter reset because the store did not know it was a gauge: perfmon_stats stores each counter's cntr_type (Darling V132 / Lite v62), the collector writes a gauge as its level with no delta, and every reader classifies by the stored type with the /sec name proxy as the pre-rung fallback (partial #3653 A7) #3708 — gauges written as their level with no delta, every reader classifies by the stored type, the /sec proxy is the pre-rung fallback; the ten Wait Statistics Average wait time (ms) PERF_AVERAGE_BULK instances are a stated finding, their average needs the base sibling this store does not join)
(PR A quiet hour halved the next hour's Query Store rate, and three trend readers recomputed an interval the store had stored all along: rollup points are rated over their own bucket width, the query-stats trends read sample_interval_seconds three-state on both SKUs, and Compose delta aggregates FILTER out the restart marker (partial #3653: A8, A11's LAG clause, the Cumulative archetype) #3695 — a rollup point is rated over its own bucket width, a raw point over its spacing; the raw Query Store arm's residual is stated on the CTE, no interval length on query_store_stats) A8 Rollup rate denominators — QueryStoreTrendRouting.cs divides by gap-to-previous-NON-EMPTY bucket (a quiet hour halves the next hour's true rate). First-point-NULL for differenced series landed in Every delta family now stores the interval its deltas accrued over, and query_stats stores the statement offsets its delta key is made of, so no restart zero reads as a measurement anywhere and the query seed can finally find its keys (#3540, V128 / Lite v61) #3630 /Zero is a measurement: health parsers say whether their source was ever seen, a regression with no baseline stays null, and the first point of a differenced trend is no longer a fabricated 0 (#3541 A12) #3642 .
A10 Baseline contamination guard is a magnitude heuristic (QUALIFY … LAG > 10000, four escape hatches); perfmon/query baselines don't read the interval they HAVE; PlanCacheAnomalyDetector.IsRealDeltaRow is the repo's one correct restart predicate and is orphaned; UTC-keyed hour-of-week buckets smear under DST. (PR The wait and perfmon baselines stop counting every restart's fabricated zero as a quiet sample: interval-honest successor aggregates replace the legacy pair in place, the provider reads whichever supply covers its window and gates the LAG heuristic on a NULL interval, the legacy pair retires by a self-executing coverage condition, the startup backfill reads coverage off the materialization, and the Daily-routed calendar answers its unmaterialized days from raw (partial #3653: A6 + A10 mechanical half) #3698 — landed the mechanical half: the three CAGG-backed arms and Lite's twins read the stored interval and gate LAG > N on sample_interval_seconds IS NULL (three-state rule); Lite's BatchRequests heuristic retired outright; IsRealDeltaRow documented as the in-memory statement of the rule the SQL tiers take from the collector's stored verdict — not orphaned, not ported; query_stats_baseline needs no successor (delta_execution_count > 0 already excludes unknowable rows); DST: Q6 in PR Hour-of-week baselines key on the target's local clock, not UTC: one shared step-function expression re-keys every arm through the scaffold, three bound clock parameters from server_properties (zone id, offset fallback, UTC when there is no row), the lookup through the same numbers — nothing keyed is stored, so re-bucketing is the next compute (#3653 item 12, Q6) #3749 — buckets key on the target's local clock via time_zone_id, offset fallback; re-bucketing is automatic at the next compute.)
A11 assorted — raw duration-trend SQL LAG-recomputes an interval query_stats has stored from the start (ViewerDataService.QueryTrends, DarlingTrendReader.QueryDurationTrendSql, Lite GetQueryDurationTrendAsync/GetExecutionCountTrendAsync; pinned "reported, not rewritten" — the LAG-recompute clause landed in PR A quiet hour halved the next hour's Query Store rate, and three trend readers recomputed an interval the store had stored all along: rollup points are rated over their own bucket width, the query-stats trends read sample_interval_seconds three-state on both SKUs, and Compose delta aggregates FILTER out the restart marker (partial #3653: A8, A11's LAG clause, the Cumulative archetype) #3695 for the Storage builder, the viewer and Lite; DarlingTrendReader.QueryDurationTrendSql (alias landed in PR The MCP query-duration trend still divided a restart's zero into 0.00 after the viewer stopped, and two sentences that #3695/#3696 made false: DarlingTrendReader's raw consts become aliases of the Storage builder, the trend trio's descriptions say which points are rated over what, and get_cpu_utilization's Darling note ports Lite's corrected cadences (partial #3653) #3700 )); pg statement delta key embeds the datid OID (DROP/CREATE reuse resumes a dead series); cpu_utilization.sample_time is target-LOCAL against UTC windows; utc_offset is offset-not-zone across DST; the deprecated Dashboard's cntr_value_per_second used integer division — the Dashboard READ is fixed (The frozen Dashboard mirrors five of today's honesty fixes in its own idiom: page counts stop posing as totals, one slow wait stops paging, a mute that matched nothing says so, an unbanded collector dot is Unknown, and the per-second counter stops truncating (#3653 Dashboard mirrors) #3658 ); the Full-edition install scripts still compute it as integer division (install/02_create_tables.sql:916, install/06_ensure_collection_table.sql:788) and install/47_create_reporting_views.sql:579-580 averages the truncated column — a schema edit on upgrade, its own decision. (Q9: install/02, 06, 47 real division + idempotent upgrade in PR Full-edition perfmon_stats.cntr_value_per_second divides as a number, not an integer; existing installs converge idempotently (#3653 item 13, Q9) #3717 ) (Q7+Q8 time-honesty rung V134 / Lite v63 in PR cpu_utilization stores each sample's UTC instant beside its server-local stamp and server_properties stores the engine's time-zone id beside its offset (Darling V134 / Lite v63), so UTC-window readers stop deriving an offset that is an hour wrong across DST (#3653 item 13, Q7 + Q8) #3730 : sample_time_utc written beside the local stamp and preferred by UTC-window readers; time_zone_id beside the offset, NULL pre-2022) (every clause landed: LAG-recompute A quiet hour halved the next hour's Query Store rate, and three trend readers recomputed an interval the store had stored all along: rollup points are rated over their own bucket width, the query-stats trends read sample_interval_seconds three-state on both SKUs, and Compose delta aggregates FILTER out the restart marker (partial #3653: A8, A11's LAG clause, the Cumulative archetype) #3695 /The MCP query-duration trend still divided a restart's zero into 0.00 after the viewer stopped, and two sentences that #3695/#3696 made false: DarlingTrendReader's raw consts become aliases of the Storage builder, the trend trio's descriptions say which points are rated over what, and get_cpu_utilization's Darling note ports Lite's corrected cadences (partial #3653) #3700 , datid Identity epochs: a target that restarted, failed over or was re-pointed no longer keeps subtracting from the old instance's counters — detection, forget and a logged, stored discontinuity on both hosts (partial #3653 A5) #3694 , Q9 install scripts Full-edition perfmon_stats.cntr_value_per_second divides as a number, not an integer; existing installs converge idempotently (#3653 item 13, Q9) #3717 , Q7+Q8 time-honesty rung V134 / Lite v63 cpu_utilization stores each sample's UTC instant beside its server-local stamp and server_properties stores the engine's time-zone id beside its offset (Darling V134 / Lite v63), so UTC-window readers stop deriving an offset that is an hour wrong across DST (#3653 item 13, Q7 + Q8) #3730 — ticked by the coordinator)
(PR A quiet hour halved the next hour's Query Store rate, and three trend readers recomputed an interval the store had stored all along: rollup points are rated over their own bucket width, the query-stats trends read sample_interval_seconds three-state on both SKUs, and Compose delta aggregates FILTER out the restart marker (partial #3653: A8, A11's LAG clause, the Cumulative archetype) #3695 — every raw-tier SUM/AVG/MIN/MAX over a delta column on a CollectorDeltaCalculator.IsDeltaFamily source carries its own FILTER (WHERE f.sample_interval_seconds IS DISTINCT FROM 0), Cumulative and the two same-table Delta measures alike; gauges, overlays, ratios, COUNT(*), qs_executions and the CAGG route untouched) Compose MeasureCatalog Cumulative archetype — all ten families now carry sample_interval_seconds (Every delta family now stores the interval its deltas accrued over, and query_stats stores the statement offsets its delta key is made of, so no restart zero reads as a measurement anywhere and the query seed can finally find its keys (#3540, V128 / Lite v61) #3630 ), so AVG/MIN/MAX(delta) can emit IS DISTINCT FROM 0 uniformly (ComposeCompiler.cs:660).
STRUCK — permanent residue, conditional on a reader that does not exist (2026-09-22; files fresh if a statement-grain reader ever appears): OversizedPlanBacklog.QueryStatsFallbackSql stays hash-keyed; an exact (plan_handle, sql_handle, offsets) join is possible for V128+ rows if a statement-grain reader appears.
Seeder test timing tolerance (PR The delta-seeder tests measured the CI scheduler, not the seeder: two clock reads bracketing the DuckDB phase gave the interval a ±2 window that failed at 123 and 243 on unrelated PRs. One whole-second anchor owns both ends now, on both hosts, and the assertions are exact — not wider. #3676 , merged 01:17Z — test-only: one whole-second anchor per test, exact asserts; the flake was two wall-clock reads straddling a >3 s DB phase, not the seeder) — LiteDeltaSeederTests.Seed_QueryStats_RestoresKeys… / Seed_PassWindow_Arms… flaked once on an unrelated PR's CI (InRange 118–122, actual 123), green on rerun. One flake is not a pattern; if it recurs the assertion's window is the fix, not the seeder.
The 10-rule measurement contract as census — rules 4 (every family persists interval — DeltaFamilyIntervalColumnTests) and 7 (every family seeded — DeltaFamilySeedingCensusTests) are pinned; rules 1–3, 5, 6, 8–10 are not. (PR The measurement contract had ten rules and two tests: the list, numbered once, and censuses for rules 1, 2, 3, 5 and 6 (partial #3653) #3701 — the list written once, numbered, in Lite.Tests/MeasurementContractCensusTests' header: 4 and 7 keep their numbers, the six named rules take 1 NULL-not-0 / 2 measured elapsed / 3 epoch / 5 rollups / 6 per-second / 8 gauges, slots 9–10 left empty — the review's enumeration is not on the record. Rules 1, 2, 3, 6 pinned (both SKUs; the Darling half sweeps Storage + Analysis, which the lite CI filter cannot reach); rule 5 rostered against the real TimescaleSupport constants — the The wait and perfmon baselines stop counting every restart's fabricated zero as a quiet sample: interval-honest successor aggregates replace the legacy pair in place, the provider reads whichever supply covers its window and gates the LAG heuristic on a NULL interval, the legacy pair retires by a self-executing coverage condition, the startup backfill reads coverage off the materialization, and the Daily-routed calendar answers its unmaterialized days from raw (partial #3653: A6 + A10 mechanical half) #3698 successors carry the predicate, query_stats_baseline's delta_execution_count > 0 is equivalent, and RollupsThatAggregateUnknowableRows names the superseded legacy pair plus query_stats_hourly / procedure_stats_hourly / query_stats_db_hourly, whose sample_count (and min() on the first two) admit a restart's (0, 0) row — a successor-CAGG lane in The wait and perfmon baselines stop counting every restart's fabricated zero as a quiet sample: interval-honest successor aggregates replace the legacy pair in place, the provider reads whichever supply covers its window and gates the LAG heuristic on a NULL interval, the legacy pair retires by a self-executing coverage condition, the startup backfill reads coverage off the materialization, and the Daily-routed calendar answers its unmaterialized days from raw (partial #3653: A6 + A10 mechanical half) #3698 's shape, not a line; rule 8 not census-able until cntr_type is stored (the missing column is pinned so the rung reds the row); rules 9–10 not recoverable. Also rostered shrink-only: fifteen ELSE 0 rate arms (dead text under their WHEREs), two PgBaselineProvider interval passthroughs defended by the successor aggregates' predicate, the five families that subtract once before the epoch carrier, and pg_wait_stats with no carrier. Stays open for the rollup roster's emptying, the gauge census once the rung lands, and rule 6's C# payload-key half.) (the fifteen ELSE 0 rate arms retired in PR Fifteen rate arms spelled "unknowable" as 0 behind a guard that happened to hide it: every guarded delta / interval now ends at END, the PostgreSQL trend readers carry the NULL rather than reading it back as 0, and both census rosters are empty (partial #3653) #3707 — roster empty) (rule 5's three hourly rollups superseded by interval-honest successors in PR Three hourly rollups counted every restart's zero as a sample and a >24 h outage left a two-hour hole under a floor that said covered: interval-honest successors beside the legacy trio with the phase grid re-derived by its own method, and a targeted refresh of each materialization hole at service start (partial #3653 — items 16 + 9, Q12 + Q10) #3731 — query_stats_interval_hourly / procedure_stats_interval_hourly / query_stats_db_interval_hourly, appended beside the legacy trio, which stays registered because the indefinite daily tier is hierarchical from it; the roster keeps the five names and now pins every member to a registered successor carrying the predicate; the phase grid re-derived by its method — heaviest :18, window 18 min, watch line 900 s, 4 s above the 896 s ceiling; the daily compression band is full at 23/23) (rule 6's C# payload-key half pinned in PR Rule 6's C# half was a stated bound, not a census: every per-second payload key on both SKUs is now traced to a reader field of its own rate name, a C# quotient over a measured span, a rate helper handed the interval, or an aggregate over a rate — a stored delta under the name is red, and the seven keys whose provenance is another name are rostered with the alias or model quotient each rests on (partial #3653 — item 16, rule 6 C# half) #3784 on both SKUs; rule 5's roster cannot empty by design — every member is pinned to a registered successor since Three hourly rollups counted every restart's zero as a sample and a >24 h outage left a two-hour hole under a floor that said covered: interval-honest successors beside the legacy trio with the phase grid re-derived by its own method, and a targeted refresh of each materialization hole at service start (partial #3653 — items 16 + 9, Q12 + Q10) #3731 , the daily successors are Daily band re-derivation is DEFERRED until a daily successor is added (band full 23/23) — the slicing arm landed in #3865 (buckets_per_batch = 1); the storm class was fixed by the WAL ceiling (#3802) #3745 's; rule 8 pinned by A falling gauge read as a counter reset because the store did not know it was a gauge: perfmon_stats stores each counter's cntr_type (Darling V132 / Lite v62), the collector writes a gauge as its level with no delta, and every reader classifies by the stored type with the /sec name proxy as the pre-rung fallback (partial #3653 A7) #3708 )
MCP payload contract (from #3541 )
A15/A16 Description drift + envelope vocabulary — audit_config claims edition-awareness with zero edition branches and lies for a PG target (no_config_data); "15-second ring buffer" wrong on-prem (the Darling 15-second note ported in PR The MCP query-duration trend still divided a restart's zero into 0.00 after the viewer stopped, and two sentences that #3695/#3696 made false: DarlingTrendReader's raw consts become aliases of the Storage builder, the trend trio's descriptions say which points are rated over what, and get_cpu_utilization's Darling note ports Lite's corrected cadences (partial #3653) #3700 ); plan top_operators silently Take(10) + estimate-basis switch; missing_indexes.impact unlabeled statement-scoped % + paste-ready DDL (collides with the no-missing-index-recs rule) — STRUCK 2026-09-20: no such repo rule exists (maintainer: DDL recommendations are legitimate product output where evidence supports them — RCSI in the analysis engine is the precedent); the misattribution led mute_analysis_finding stops writing the hash as the path and stops registering the same mute twice, remove_server can remove a server that never connected, and eight MCP descriptions stop saying what the code does not do (partial #3653 A15/A16) #3696 to DROP create_statement from missing_indexes[], which Restore missing_indexes[].create_statement on the plan tools — #3696 dropped it citing a 'no-missing-index-recs rule' that was never made; DDL recommendations are legitimate product output (RCSI precedent) #3805 restores; the AG tool hides its reader's two documented traps; DarlingWebEndpoints.cs:1959 describes the duration trend as "percentiles"; 7 truncation dialects / 5 severity vocabularies ("NoData" and "No Data" in one payload) (vocabulary collapse landed in PR One payload said NoData and No Data, eight spellings of four severity words were two bands and six other vocabularies, and the page cut had five names: the daily summary speaks one band token on both SKUs, six inferred cuts become observed truncated + *_returned, and the census classifies every cut key and severity literal by what it is (partial #3653) #3703 — severity one spelling, page cuts one dialect, source-side cuts classified; errors-one-shape awaits Q11; residue on the PR: the PG severity-token ladder is a different vocabulary (band + reason split is a ruling), truncated carries the get_query_store_top reports a window it cannot serve: raw query_store_stats is dropped at 4 days #2364 window-floor fact on the trend family, shown fenced by PgTarget tests); StampedUnderCollectionTime tools (get_database_sizes, get_running_jobs, get_server_properties, get_session_stats — read by server-tabs.js by the old key); resource-semaphore latest-read ORDER BY drift between SKUs; mute_analysis_finding writes story_path = story_path_hash and registers duplicate mutes; remove_server cannot remove a server that never connected (resolves against servers, not config_monitored_servers). (Lite trend descriptions + unrated_note + latch/spinlock parity in PR 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 ; window_truncated NOT done — ~15 consumers incl. get_query_store_top in DarlingMcpDataTools.cs, counted on the PR) (errors-one-shape in PR MCP errors take one wire shape: FormatError returns the {status:"error"} envelope on both SKUs (Partial #3653, Q11) #3719 ) (window_truncated rename delivered in PR The trend family's window floor is window_truncated on both SKUs, not 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 on both SKUs — the vocabulary census now classifies it; remaining: the two stored-interval spellings, inventoried on the PR) (2026-09-20 A15 lane, verified at source on dev 32096b6 : all six remainder clauses were already delivered — audit_config says what it is and refuses a PostgreSQL target with not_collected (PostgreSQL targets enter the analysis pipeline: the pass routes by registry engine_kind inside the service, a pg_database_stats coverage witness replaces the tombstone, and every shared switch gains its one pg_ arm so the content lanes can build in parallel (#3542 v1 plumbing) #3665 envelope, mute_analysis_finding stops writing the hash as the path and stops registering the same mute twice, remove_server can remove a server that never connected, and eight MCP descriptions stop saying what the code does not do (partial #3653 A15/A16) #3696 description); top_operators publishes operators_cap / operators_returned / total_operators / truncated / operators_ranked_by, and missing_indexes[] carries impact_basis with the CREATE INDEX paste REMOVED from the MCP surface (mute_analysis_finding stops writing the hash as the path and stops registering the same mute twice, remove_server can remove a server that never connected, and eight MCP descriptions stop saying what the code does not do (partial #3653 A15/A16) #3696 — the plan viewer still renders the DMV text; whether the MCP surface stays DDL-free is the maintainer's ruling); the AG description and the instructions paragraph name the reader's two traps (mute_analysis_finding stops writing the hash as the path and stops registering the same mute twice, remove_server can remove a server that never connected, and eight MCP descriptions stop saying what the code does not do (partial #3653 A15/A16) #3696 ); the catalogue line says rates, not percentiles (mute_analysis_finding stops writing the hash as the path and stops registering the same mute twice, remove_server can remove a server that never connected, and eight MCP descriptions stop saying what the code does not do (partial #3653 A15/A16) #3696 ); server-tabs.js reads the four stamped tools by captured_at (The server page labelled page sums as totals, drew one instant as a trend, and four tools called their capture time by another name: get_pg_database_stats' totals ride the row statement, the memory-grant pair renders window[] beside its newest snapshot, get_pg_io_stats' page count is combinations_returned, and four latest reads stamp captured_at on both SKUs (partial #3653) #3697 ). PR A15/A16 remainder: the six named drifts were already landed (#3665/#3696/#3697) — this pins the three that had no pin: get_ag_health names its reader's traps, the catalogue's duration-trend line says rates not percentiles, server-tabs.js reads the Stamped tools by captured_at (#3653) #3801 adds the three pins those fixes lacked (AG description + instructions, catalogue line + serializer + whole-catalogue sweep, the JS tile derived from the stamp roster). Rulings left on the face, not work: the get_resource_semaphore interval spelling — null-when-unknowable under sample_interval_seconds beside interval_known, vs the raw stored 0 on get_file_io_stats / get_perfmon_trend and interval_seconds on the latch/spinlock pair; one fact, three spellings, each described truthfully at its site, inventoried on The trend family's window floor is window_truncated on both SKUs, not 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 — unify or leave; and the PG severity-token ladder's band + reason split (One payload said NoData and No Data, eight spellings of four severity words were two bands and six other vocabularies, and the page cut had five names: the daily summary speaks one band token on both SKUs, six inferred cuts become observed truncated + *_returned, and the census classifies every cut key and severity literal by what it is (partial #3653) #3703 ).)
wwwroot twins — server-tabs.js:3112-3121 tile labels for get_pg_database_stats' page-summed totals (the A7 census's one stated allowance; deleting the allowance is the done signal); get_pg_io_stats.combination_count kept for server-tabs.js:3173; server-tabs.js:689 draws get_memory_grants.grants[] (one instant) as a LINE chart under a window subtitle — chart the window[] block Every latest-snapshot MCP read says when it was captured, and no tool accepts a window it does not read (#3541 A10) #3637 added. (delivered in PR The server page labelled page sums as totals, drew one instant as a trend, and four tools called their capture time by another name: get_pg_database_stats' totals ride the row statement, the memory-grant pair renders window[] beside its newest snapshot, get_pg_io_stats' page count is combinations_returned, and four latest reads stamp captured_at on both SKUs (partial #3653) #3697 , e6b1ff5 : get_pg_database_stats publishes window totals on the statement and the A7 census allowance is DELETED — McpPageContractTests now enforces it; combination_count → combinations_returned; get_memory_grants' grants[]/window[] rendered as two tables, window first; the four StampedUnderCollectionTime tools read by captured_at. Ticked by the coordinator 2026-09-20 after verifying each clause at source on dev 050b50e — the box was stale, not the work.)
(PR Six small honesty riders from the brains-review residue: a retired knob says so on the wire, get_pg_server_config says when it was captured, Lite's latch/spinlock pages bind to the caller's limit, the ORDER-BY-integer fold gets a census, and two rung docs stop asserting superseded facts (#3653) #3659 — regex widened to GetCurrent*Async, get_pg_server_config stamped and in DarlingOnlyStamped; the object-stats trio below is NOT done and stays in UnstampedLatestReadsPendingA10) A10 residual — McpLatestSnapshotStampTests' reader-call regex does not match GetCurrentConfigAsync, so get_pg_server_config (an unstamped latest-snapshot read) sits OUTSIDE Every latest-snapshot MCP read says when it was captured, and no tool accepts a window it does not read (#3541 A10) #3637 's census; a sibling lane added a DarlingOnlyStamped roster allowance but did not widen the regex — widen it and stamp the tool. Object-stats trio unstamped (get_index_usage, get_object_locking, get_table_index_sizes; per-database daily snapshot → per-row stamp), held in the census as UnstampedLatestReadsPendingA10 (shrink-only).
(PR Six small honesty riders from the brains-review residue: a retired knob says so on the wire, get_pg_server_config says when it was captured, Lite's latch/spinlock pages bind to the caller's limit, the ORDER-BY-integer fold gets a census, and two rung docs stop asserting superseded facts (#3653) #3659 ) A3 class on Lite — LocalDataService.LatchSpinlock.cs:129/261 LIMIT 20 literal caps behind get_latch_stats/get_spinlock_stats (also the viewer grid).
Viewer calendar port of The daily summary stops painting purged months green, and every MCP filter is part of the query: parallel_only/min_dop/blocking_only cut before the page, a negative window is refused, an unknown source names the accepted set (#3541 A9/A13) #3641 's retention states (PR The Darling viewer's trend charts and calendar tell the same truth the MCP tools learned today: routed by retention tier and saying so, no fabricated first point, and a purged day is grey, not green (#3653 viewer ports) #3666 ; the "per-signal nulling of purged counts" clause stays open) — ViewerDataService.DailySummary.cs reads the same SQL and now receives signal_sources_present; its port is HasData = DailySummaryRetention.StateFor(...) == Collected plus the horizon read. DailyHealthBandCalculator.Describe says "No data collected." for any HasData = false — wording for the purged / past_horizon states is Blocking and CPU bands are rates over the window they were measured in, tiered from 14 days of fleet data, so a week of blocking and an hour of blocking stop getting the same colour (#3539 A2/A3/A8d) #3596 's file. Per-signal nulling of purged counts (the honest end state; past_horizon discloses the ambiguity instead) is a lane of its own. ViewerDataService.QueryTrends.cs QueryDurationTrendSql / QueryStoreDurationTrendSql / execution-count copies still carry ELSE 0 (Zero is a measurement: health parsers say whether their source was ever seen, a regression with no baseline stays null, and the first point of a differenced trend is no longer a fabricated 0 (#3541 A12) #3642 fixed the MCP readers; the procedure copy went in Every delta family now stores the interval its deltas accrued over, and query_stats stores the statement offsets its delta key is made of, so no restart zero reads as a measurement anywhere and the query seed can finally find its keys (#3540, V128 / Lite v61) #3630 ); PvsTrendPoint.PvsSizeMb coerces NULL to 0 on the top-5 trend series (both SKUs, rare) — Lite + the Darling Viewer chart in The Darling viewer's trend charts and calendar tell the same truth the MCP tools learned today: routed by retention tier and saying so, no fabricated first point, and a purged day is grey, not green (#3653 viewer ports) #3666 ; the Darling MCP reader (DarlingPvsReader, get_pvs_stats trend arm) in PR Darling's get_pvs_stats trend drew an unmeasured pass as 0 MB: the point is now null with pvs_measured beside it, the same two keys Lite's twin has carried since #3666, and a measured zero is still a zero (#3653) #3682 with pvs_measured per point.
Viewer Performance Trends tab (PR The Darling viewer's trend charts and calendar tell the same truth the MCP tools learned today: routed by retention tier and saying so, no fabricated first point, and a purged day is grey, not green (#3653 viewer ports) #3666 , merged 1de06bd — routed via the shared DurationTrendRouting) had the same raw-only read the MCP trio had before The duration-trend trio routes by retention tier and discloses its source like get_query_trend already does, so a 7-day request no longer returns 4 days labelled quiet (#3541 A2) #3590 (ViewerDataService.QueryTrends.cs:239-246); a 7-day chart on a TimescaleDB store plots 4 days. Port = DarlingTrendReader.*HourlySql + ResolveTier.
(PR The MCP trend reader's tier ladder and hourly SQL were pinned EQUAL to the Storage definitions #3666 moved them to, which fails only after two copies drift — they are now aliases; and the Query Store duration chart, the one Performance Trends chart left untitled, says what served it and where the rollup's floor cut the window's head (#3653) #3684 ) The Darling viewer's trend charts and calendar tell the same truth the MCP tools learned today: routed by retention tier and saying so, no fabricated first point, and a purged day is grey, not green (#3653 viewer ports) #3666 's two stated leftovers — DarlingTrendReader's ladder/hourly-SQL members become ALIASES of DurationTrendRouting (the The Darling viewer's trend charts and calendar tell the same truth the MCP tools learned today: routed by retention tier and saying so, no fabricated first point, and a purged day is grey, not green (#3653 viewer ports) #3666 equality pin → an identity pin over the declarations; zero behaviour, proven by a before/after reflection dump of the built dll), and the Query Store duration chart — the one Performance Trends chart The Darling viewer's trend charts and calendar tell the same truth the MCP tools learned today: routed by retention tier and saying so, no fabricated first point, and a purged day is grey, not green (#3653 viewer ports) #3666 left untitled — discloses its route word and the corrected rollup's unserved floor (QueryStoreTrendRouting.SourceWord / UnservedBefore, one rule the MCP tool and the chart both read). Lite needs no twin: v_query_store_stats unions live + parquet at one grain, no floor to disclose.
(PR Six small honesty riders from the brains-review residue: a retired knob says so on the wire, get_pg_server_config says when it was captured, Lite's latch/spinlock pages bind to the caller's limit, the ORDER-BY-integer fold gets a census, and two rung docs stop asserting superseded facts (#3653) #3659 — zero hits on either SKU; the fact sweeps every Storage *Reader field and Lite/Services/*.cs) ORDER BY integer-expression sweep — ORDER BY 3 + 4 DESC folds to a constant in PostgreSQL (MCP percents name their denominator: shares are of the window or say they are of the page, so a three-row page stops summing to 100% of everything (#3541 A7) #3613 found get_pg_kernel_stats had never been ranked); one fact in DarlingPgReadSqlParsesLiveTests grepping every DarlingPg*Reader const for ORDER BY\s+\d+\s*[-+*/]\s*\d+ closes the class.
The 11-rule payload contract as census — the page dialect (MCP pages now say what bounded them: caps bind to the caller's limit, truncation is detected not inferred, and no page count is called a total (#3541 A3) #3594 ), percent denominators (MCP percents name their denominator: shares are of the window or say they are of the page, so a three-row page stops summing to 100% of everything (#3541 A7) #3613 ), latest-is-a-time (Every latest-snapshot MCP read says when it was captured, and no tool accepts a window it does not read (#3541 A10) #3637 ), zero-is-a-measurement (Zero is a measurement: health parsers say whether their source was ever seen, a regression with no baseline stays null, and the first point of a differenced trend is no longer a fabricated 0 (#3541 A12) #3642 ) and filters-in-the-query (The daily summary stops painting purged months green, and every MCP filter is part of the query: parallel_only/min_dop/blocking_only cut before the page, a negative window is refused, an unknown source names the accepted set (#3541 A9/A13) #3641 ) are pinned; the remainder (name = truth, one vocabulary, errors one shape, refuse what you cannot honor) are not; a shared page-bounds helper in PerformanceMonitor.Common/Mcp/McpHelpers.cs is the code-side home. (PR Three PostgreSQL pages observe truncation off limit + 1 through one shared helper, the census sweeps the inference class instead of a roster, and four payload rules become facts and inventories, so three tools stop guessing truncation from a full page and four of the eleven payload rules stop being sentences (partial #3653) #3699 — McpPayloadContractCensusTests + McpHelpers.BoundPage landed: refuse what you cannot honor pinned (every bounded parameter reaches a shared validator or one of five rostered inline days_back refusals; no parameter is clamped; the timestamp arm's two general TryParse sites rostered), errors one shape pinned as "every catch returns through a shared shape" with the one ad-hoc site rostered — but the two shared shapes (a bare string and a JSON envelope, 21 PG files on the envelope) are an INVENTORY for the vocabulary lane, the rule itself is not met; name = truth 's two greps pinned as inventories (10 total_* = .Count whole-set sites; latest-anchored readers project their anchor bar the two known A10 residuals) and the rest stated as review-checklist; one vocabulary rostered shrink-only (14 truncation-key dialects, 8 severity-word spellings in three casings, the NoData/No Data pair by named instance, 7 statement-terminal literal LIMITs all Lite viewer-side) for the wave-3 vocabulary lane; writes report what happened already pinned per tool by MCP write tools report what happened: every server you add lands in a counter, a partial name cannot delete the wrong server, an omitted field is never a clear, and a mute that matched nothing says so (#3541 A14) #3615 , a cross-write census deferred behind the envelope lane's mute rewrite. The page-inference sweep now covers every tool body on both SKUs.) (errors one shape: every failure is the JSON envelope through FormatError, PR MCP errors take one wire shape: FormatError returns the {status:"error"} envelope on both SKUs (Partial #3653, Q11) #3719 )
Frozen-twin mirror batch (deprecated/Dashboard, bug-fix support — one lane, smallest faithful diff each)
Mcp/McpBlockingTools.cs:68,133, McpAlertTools.cs:104 — total_events/total_deadlocks/total_alerts are page counts (MCP pages now say what bounded them: caps bind to the caller's limit, truncation is detected not inferred, and no page count is called a total (#3541 A3) #3594 's class; caps live in the Dashboard's own readers).
MainWindow.AlertEngine.cs:477 + Services/DatabaseService.NocHealth.cs:877 — the avg-ms poison-wait loop over live SQL (SQL Server's Poison Wait alert measures accumulated starvation over a ten-minute window like its PostgreSQL twin, so one slow wait no longer pages and a THREADPOOL storm no longer sleeps (#3539 A4) #3593 's class).
Mcp/McpAnalysisTools.cs:~429 — mute_analysis_finding returns "muted" for any hash (MCP write tools report what happened: every server you add lands in a counter, a partial name cannot delete the wrong server, an omitted field is never a clear, and a mute that matched nothing says so (#3541 A14) #3615 's class); compare keeps the ±0.1 band (compare_analysis bands each delta by the server's own dispersion and folds one cause into one row, so same-hour-yesterday noise stops reading as a verdict (#3538 A3) #3634 's class); no confidence_basis (Story confidence measures corroboration instead of path length, and the nightly rebuild's three cards become one incident that names the job (#3538 A6/A9) #3632 ).
Models/ServerHealthStatus.cs:510 — own CollectorSeverity with (0,0) → Healthy (A server nothing has banded yet is Unknown, not Healthy, and the alert-history grids show the severity the alert actually fired at instead of the colour its name implies (#3539 A6/A8e) #3635 's class).
STRUCK — decision, not work (2026-09-22): Analysis/SqlServerFactCollector.Waits.cs:54,121,178 — nominal-window divisor (Analysis facts divide by the time the collector actually observed, and a window with a hole in it says so, so collector downtime no longer reads as improvement (#3538 A2) #3592 's class) — declined : needs a T-SQL coverage witness plus its own service/MCP surfaces, not a mirror. Analysis/SqlServerAnomalyDetector.cs:38,74 — old HAMMERDB markers (done in The frozen Dashboard mirrors five of today's honesty fixes in its own idiom: page counts stop posing as totals, one slow wait stops paging, a mute that matched nothing says so, an unbanded collector dot is Unknown, and the per-second counter stops truncating (#3653 Dashboard mirrors) #3658 ).
Batch landed in #3658 (c7ca1ad ): items 1–4 and the anomaly-detector markers; the Waits.cs divisor stays declined and open as stated. compare_analysis ±0.1 band / confidence_basis deliberately not mirrored (semantics upgrades, not bug fixes on a frozen twin).
Not here
Status: a work queue, being worked easiest-first. Nothing here waits on a ruling or a prerequisite — no ruling from the maintainer is pending, no prerequisite PR is open, and every item below is independent of the others. Each item is a lane of its own (S/M for the mechanical ones, multi-day for the four marked structural). It stays open until its checklist is empty; items marked structural carry a one-line reason for staying and are the last to go.
The 2026-09 brains-review campaign (five adversarial lanes graded against the OtterTune corpus; wave 1 #3523–#3563, wave 2 #3538–#3541 + #3526/#3540 keystone) closed its four lane issues on 2026-09-18 with 21 wave-2 PRs merged in one day. This issue holds what was deliberately deferred — every item below is structural or multi-day by the reviews' own sizing, or is a frozen-twin mirror batched here so it lands as one faithful diff. Nothing here is forgotten; each line names its evidence and its shape. Measured inputs (sanitized) are in the wave-2 measurement record on the design bus (
pm-issue-queue-inbox/design/), filed beside the PG-analysis v1 plan.Analysis engine (from #3538)
as_ofpass mechanically reports more anomalies than the 4 h scheduled pass on identical behaviour, and the IO detector uses AVG while its siblings use MAX under shared cutoffs (PgAnomalyDetector.cs,AnomalyThresholds.cs:22). Shape: baseline the statistic you test (per-window peaks per bucket), or gate on peak AND window mean, or an N-aware threshold term. Structural; needs a 30-day planted series to validate. (Q1 first slice in PR The anomaly gate judges the window peak AND the window mean, so one hot sample stops reading as an anomaly and a longer window stops buying more of them; I/O reads the pair like its siblings (Partial #3653 — item 1, Q1) #3724: every detector gates on window peak AND mean under the existing cutoffs; IO on the same pair; the per-window-peak-per-bucket family waits for the ~day-5 re-read)server_config_changes/database_config_changes/trace_flag_changeshave zero references in either analysis project;ForcePlanSelfReviewproves the verify-after pattern in-repo. Shape (engine review C2): on a new change row in the worker's AN3 block, runComparePeriodsAsync(now dispersion-banded, compare_analysis bands each delta by the server's own dispersion and folds one cause into one row, so same-hour-yesterday noise stops reading as a verdict (#3538 A3) #3634) around the change timestamp and emit aCONFIG_CHANGEDfinding with sigma-scaled deltas. The single biggest credibility upgrade available; medium risk; its own design pass. (Q2 delivered in PR A server configuration change now has a consequence the engine states: CONFIG_CHANGED compares the ±4 h around it and says what moved, or that nothing did (Partial #3653 — item 2, Q2) #3720 — server config slice: oneCONFIG_CHANGEDInformation finding per change event on both SKUs, ±4 hComparePeriodsAsyncbanded byComparisonBanding, prose says "first observed" and discloses the partial after half; there is NOserver_config_changestable — changes areConfigChangeDiffover the on-connectserver_configsnapshots; database config + trace flags: SLICE TWO DELIVERED in PR CONFIG_CHANGED spoke only for sp_configure: a database option or a trace flag changing inside the window was a history row and no finding. The same fact now covers all three snapshot families, and changes the connect observed together are one card (Partial #3653 — item 2, A10 slice two) #3786 (12e7d2e85, 2026-09-20) — the sameCONFIG_CHANGEDfact now covers all three snapshot families, with changes observed together at one connect rendered as one card; item complete, both families shipped)RUNNING_JOBScarries no job name (Pg/DuckDbFactCollector.Activity.csemit doubles only;Fact.ObjectNamecould carry it — collector change both SKUs). (the RUNNING_JOBS job-name clause landed in PR The analysis said an Agent job was running long and could not say which: the RUNNING_JOBS fact now names the job furthest past its own history on both SKUs, and the card, the frozen finding text and the headline say which one (partial #3653) #3693; recurrence/moving-window await ruling Q3) (recurrence label + moving-window label in PR A chain that fires every Tuesday at the same hour was rated a fresh incident each week and nobody was told: the pass now labels it "recurring at this hour" at unchanged severity, and a weekly Agent job whose slot slid gets "maintenance window moved" (partial #3653 item 3, Q3) #3725 — Q3 ruled LABEL at unchanged severity)ObservedDurationMs <= 0alone, both arms byte-identical; an observed window with no facts runs the pass and readsemptyat its stated coverage,WindowEmptyMessagenull so the worker clears its marker; one Information line names the coverage for the scheduled pass; cross-SKU parity pin in Darling.Tests) Empty-window rule wording, both SKUs —DarlingAnalysisService.cs/Lite/Analysis/AnalysisService.cs:facts.Count == 0 || ObservedDurationMs <= 0says "collection appears to have stopped" even when the coverage witness proves the window WAS observed and simply emitted no fact (now that every collector stamps coverage, Analysis facts divide by the time the collector actually observed, and a window with a hole in it says so, so collector downtime no longer reads as improvement (#3538 A2) #3592/PostgreSQL targets enter the analysis pipeline: the pass routes by registry engine_kind inside the service, a pg_database_stats coverage witness replaces the tombstone, and every shared switch gains its one pg_ arm so the content lanes can build in parallel (#3542 v1 plumbing) #3665). Shape: gate onObservedDurationMs <= 0alone; keep the "point-in-time facts only" prose for the observed-zero case. Twin edit, S.DefaultRatioThresholduncalibrated (MEASURED 2026-09-22: 3,655 windows, ratio p99 = 3.14, p99.9 = 28.3, ≥ 4.0 on 0.63 % — 4.0 stays, lineage paragraph +threshold_lineage = 1ride the next lane through AnomalyThresholds.cs; scheduler-count clause RESOLVED 2026-09-22: the column iscpu_count(4–16 cores, median 8 on the primary fleet); per-scheduler normalisation CANCELS out of the ratio — numerator and its own-baseline denominator divide by the same constant — so the ratio bar is unchanged; the one place core count is real is the ABSOLUTE magnitude floor (500 ms/s means 4× different saturation on 4 vs 16 cores) — a design note, not a recalibration; whether that floor becomes per-core rides the next lane in AnomalyThresholds.cs as a doc paragraph) — one more column on the fleet wait pass (the ratio's own per-server-window distribution). Scheduler-count normalisation of the wait fraction (same fraction means different things on 4 vs 64 cores) — documented on the table; needs its own read.DarlingMcpTools.csis a one-line follow-up owned by the PostgreSQL-target analysis engine: design and v1 build #3542 lane's coordinator, the Dashboard twin belongs to the frozen-twin batch below)ToolRecommendationshas noHADR_SYNC_COMMITentry (Lite/Darling/Dashboard) since The scorer's wait thresholds carry their measurement: WRITELOG stops saturating on a third of routine windows, deadlocks grade 5/hr apart from 90/hr, and the absolute gates scale with the hours actually observed (#3538 A5/A7) #3616 gave it a scorer entry; drill-down collectors attach nothing for it.Alert semantics (from #3539)
program_name/wait_typebreakdown is the follow-up read). (tempdb Space K = 3 delivered in PR tempdb Space fired on one collected sample and resolved on the next: the arm now holds for 3 consecutive collections behind the shared persistence gate, counted per collection rather than per sweep, with no-data freezing it instead of resolving to N/A (#3653 A5, tempdb slice) #3692; Q4 in PR Blocking Wait Time fires on one snapshot at 3× the bar or on 3 consecutive collections, not on every snapshot; Long-Running Query gets an opt-out knob seeded from the production read — job-step program prefix, the two NT AUTHORITY logins — applied ahead of the row cap and counted by arm (#3653 A5, Q4 + Q5) #3734: single snapshot at 3× fires at once, else 3 consecutive collections through the shared gate; Q5 opt-out knob seeded from the production read — job-step program prefix + the two NT AUTHORITY logins — in PR Blocking Wait Time fires on one snapshot at 3× the bar or on 3 consecutive collections, not on every snapshot; Long-Running Query gets an opt-out knob seeded from the production read — job-step program prefix, the two NT AUTHORITY logins — applied ahead of the row cap and counted by arm (#3653 A5, Q4 + Q5) #3734, Darling store home + MCP twins follow as V135 in PR The Long-Running Query opt-out knob gets its Darling store home: V135 adds the two lists to config_alert_settings with the production read's seeds as their DEFAULT, wired through the service, the Viewer, the probe and both SKUs' get_alert_settings / update_alert_settings twins (#3653 A5, Q5) #3736, merged dc1ddeb; the "what are they" read landed and seeded the defaults — ticked by the coordinator) Q5's PostgreSQL twin: The PostgreSQL Long-Running Query twin ignores the program/login opt-out knob: the setting is visible, editable and read back, and does nothing on a PostgreSQL target #3743 → PR The PostgreSQL Long-Running Query twin honours the program/login opt-out knob, so a setting an operator can see, edit and read back on a PostgreSQL target stops doing nothing there — shared predicate builder over application_name/usename, ahead of the row cap, counts by arm on the card (#3743, partial #3653) #3770 (the PG LRQ read honours the same knob through the shared predicate builder, ahead of the row cap).Severity: null(AlertEngine.cs:650, 1135, 1489); the grids now render whatever the engine grades (A server nothing has banded yet is Unknown, not Healthy, and the alert-history grids show the severity the alert actually fired at instead of the colour its name implies (#3539 A6/A8e) #3635), so grading is engine work. A8d's 20% collector Critical bar is by analogy, not measured.non_default_countis the snapshot's viaCOUNT(*) FILTER … OVER ()above the LIMIT,non_default_returnedis the page's,include_defaultsmoved into the statement,truncatedobserved offlimit + 1; the three sibling pages in the same file — extensions, lock stats, config changes — observe too. Three MORE>= limitsites remain in OTHER files:DarlingMcpPgDeadlockTools.cs:82,DarlingMcpPgPlanTools.cs:210(BuildReadinessJson),DarlingMcpPgIndexTools.cs:150, each against its own pins) (the three remaining >= limit sites fixed in PR Three PostgreSQL pages observe truncation off limit + 1 through one shared helper, the census sweeps the inference class instead of a roster, and four payload rules become facts and inventories, so three tools stop guessing truncation from a full page and four of the eleven payload rules stop being sentences (partial #3653) #3699, plusget_pg_plans' unannouncedTake(limit)beside them; all four throughMcpHelpers.BoundPage)get_pg_server_configA3 residue —DarlingMcpPgServerStateTools.cs:~640:truncated = rows.Count >= limit(inferred, the MCP pages now say what bounded them: caps bind to the caller's limit, truncation is detected not inferred, and no page count is called a total (#3541 A3) #3594 class) andnon_default_countis page-scoped (counted over fetched rows, not the snapshot). Darling-only tool. S. (Correction from Six small honesty riders from the brains-review residue: a retired knob says so on the wire, get_pg_server_config says when it was captured, Lite's latch/spinlock pages bind to the caller's limit, the ORDER-BY-integer fold gets a census, and two rung docs stop asserting superseded facts (#3653) #3659: PG config is collected HOURLY —CollectorScheduleDefaults["pg_server_config"] = (60, 365)— not on connect; the connect-time shape is SQL Server's family.)ServerHealthClassifier.CpuSeverity, deadlocks onGradeDeadlockFire+ the store'sDeadlockRateThresholds, blocking/LRQ explicit Warning-only with the missing bar named) PG host arms still fireSeverity: null—DarlingWorker.EvaluatePgDeadlocksAsync,EvaluatePgBlockingAsync,EvaluatePgLongRunningQueryAsync(:4600) and:4057): the A8e class The PostgreSQL poison-wait host holds on silence like the SQL Server engine does, and three presence-flat alerts grade their severity from the bars the health bands already measured (#3653 alert riders) #3660 fixed on the SQL Server engine. PG CPU already computes the ACU capacity percent (EvaluatePgCpuAsync(FleetCpuProvenance.CpuBandInputPercent) soServerHealthClassifier.CpuSeveritygrades it directly; PG deadlocks can reuseGradeDeadlockFirewithDeadlockRateThresholds. READMEs say "not yet graded". S.lock_waitlog events are the event-grain source a report-rate band would read. (READ SPEC on the bus 2026-09-22 09:05Z —pg_log_eventslock_wait reports per cluster-hour since.450+log_lock_waitscoverage; decision rule stated there: adopt the SQL Server tiers by reference if the shape is bimodal and ≥ 80 % of clusters log;unobservablefor clusters with logging off; defer to the 14-day mark if < ~500 logged cluster-hours. DECIDED 2026-09-22 09:00Z:log_lock_waitsis OFF on 50/50 clusters; zero lock_wait rows ever (parser fine — 388 K error-family rows prove the pipe); sampled edges show blocking DOES occur (p99 14 captures/cluster-hour) — a real observability gap. Band stays Unknown fleet-wide by design; PostgreSQL blocking family: an operator now learns who headed a blocking chain, in what state, and how long the sessions behind it had really waited — PG_BLOCKING_CHAIN from the sampled edges, PG_LOCK_WAIT_EVENTS from the engine's own log lines, PG_LONG_RUNNING_QUERY from the active sessions, and a blocked-sessions anomaly whose baseline counts a quiet capture as zero (partial #3691, lane 17) #3750'sPG_LOCK_WAIT_EVENTSalready sayslog_lock_waits is offrather than 0 andPgTargetFactCollector.Blocking.cs:334reads the setting, so the product side is complete. The lever is infra: one RDS parameter-group change (negligible overhead — logs only waits crossing deadlock_timeout), after which the 14-day calibration clock starts and the SQL Server tiers are adopted by reference if the shape is bimodal. Not a lane.)DarlingWorker.EvaluatePgPoisonWaitAsync) while the SQL engine holds since SQL Server's Poison Wait alert measures accumulated starvation over a ten-minute window like its PostgreSQL twin, so one slow wait no longer pages and a THREADPOOL storm no longer sleeps (#3539 A4) #3593 (unwatched ≠ quiet) — adopt the observed-window clear.get_alert_settings.poison_wait.threshold_msis reported and accepted but unconsulted since SQL Server's Poison Wait alert measures accumulated starvation over a ten-minute window like its PostgreSQL twin, so one slow wait no longer pages and a THREADPOOL storm no longer sleeps (#3539 A4) #3593 — deprecation note on the wire.PgMigrations.csV79 / V122 rung docs carry pre-Alert-condition semantics campaign: window-invariance, persistence gates, engine parity (8 findings + parity table) #3539 statements ("grew N MB inside the window"; "a PostgreSQL server has no deadlock band") — historical rung rationale; one "(ended by #…)" clause each, next time the file is edited.Measurement layer (from #3540)
sqlserver_start_timecollected but consumed only for display; AG failover clears alert state but never delta baselines;pg_stat_statements_reset()invisible (pg_stat_statements_info.stats_resethas zero references). Shape: per-server epoch check (start_time / stats_reset / server_name) →ClearServer+ a discontinuity marker the read layer renders. AlsoDarlingWorker.ReconcileServers"definition changed" branch reconnects under the sameserver_idwithout clearing baselines. The only genuinely structural measurement item. (mechanical slice — epoch comparator, persisted prior,ClearServer/ClearGroupsbefore the subtraction,collection_logmarker, theReconcileServersforget — in PR Identity epochs: a target that restarted, failed over or was re-pointed no longer keeps subtracting from the old instance's counters — detection, forget and a logged, stored discontinuity on both hosts (partial #3653 A5) #3694, merged 3386f2b; carrier-order and Aurora-wait residues closed in PR Five families subtracted from the old instance once before anyone noticed it had changed; Aurora's wait counters had no one watching for a restart at all: wait_stats carries the SQL Server identity first in the order, pg_wait_stats carries pg_postmaster_start_time(), and both census rosters are empty (partial #3653) #3705:wait_statscarries the SQL Server pair first in both hosts' order,pg_wait_statscarriespg_postmaster_start_time(), both census rosters empty. Remaining clause: the rendered marker.) (rendered marker:discontinuities[]on every trend payload + viewer/web notice, both SKUs, PR The store marked every identity epoch and no surface rendered it: every trend payload on both SKUs now ends with discontinuities[], the two desktop viewers draw the marker, and the web trend panels say the sentence (partial #3653 A5) #3787)days_missing; the >24 h hole repair delivered in Three hourly rollups counted every restart's zero as a sample and a >24 h outage left a two-hour hole under a floor that said covered: interval-honest successors beside the legacy trio with the phase grid re-derived by its own method, and a targeted refresh of each materialization hole at service start (partial #3653 — items 16 + 9, Q12 + Q10) #3731. What remains is ONLY the legacy hourly trio's interval-honest successor, which needs three new aggregates on a phase grid that has 4 s of margin against its 896 s ceiling — grid-coupled, and Daily band re-derivation is DEFERRED until a daily successor is added (band full 23/23) — the slicing arm landed in #3865 (buckets_per_batch = 1); the storm class was fixed by the WAL ceiling (#3802) #3745 closed dormant, so this waits on grid capacity, not on a decision.) —TimescaleSupport.CreateWaitStatsBaselineSqlstill sums the restart zero and counts it as a sample; a CAGG cannot be altered in place and a rebuild forfeits ~35 days of baseline history. Shape: new aggregate under a new name withWHERE sample_interval_seconds IS DISTINCT FROM 0,WITH NO DATA+--backfill-rollups, retire the old viaRetiredBaselineRelations(Retire the orphaned cpu_utilization_baseline / file_io_baseline continuous aggregates (#1995 cleanup) #2007). Plus the >24 h host-outage permanent unmaterialized hourly hole (RetentionTierRouterself-discloses) and the daily-tier calendar'sunique_queries = 0beside fresh raw numbers. (PR The wait and perfmon baselines stop counting every restart's fabricated zero as a quiet sample: interval-honest successor aggregates replace the legacy pair in place, the provider reads whichever supply covers its window and gates the LAG heuristic on a NULL interval, the legacy pair retires by a self-executing coverage condition, the startup backfill reads coverage off the materialization, and the Daily-routed calendar answers its unmaterialized days from raw (partial #3653: A6 + A10 mechanical half) #3698 — landed:perfmon_interval_baseline/wait_stats_interval_baselineREPLACE the legacy pair inBaselineAggregates(an append moves the phase grid: watch line 1,050→950 s vs the 896 s ceiling), the legacy pair retires throughSupersededBaselineRelationswhen the successor covers the tier (~day 5, judged every start), the startup backfill now fires on the first start (its gate read coverage through the real-time view —-infinitywatermark — and skipped), and the Daily-routed calendar answers the days past the rollup's ceiling from raw; remains: the >24 h hole (read honestly intoRetentionTierRouter— it is the ≤2 h pre-outage tail, not the outage; repair delivered in PR Three hourly rollups counted every restart's zero as a sample and a >24 h outage left a two-hour hole under a floor that said covered: interval-honest successors beside the legacy trio with the phase grid re-derived by its own method, and a targeted refresh of each materialization hole at service start (partial #3653 — items 16 + 9, Q12 + Q10) #3731 asRepairMaterializationHolesAsync), and NULL-not-0 for a day skipped BELOW the rollup ceiling (payload-shape, census lane).) (Q10 delivered in PR Three hourly rollups counted every restart's zero as a sample and a >24 h outage left a two-hour hole under a floor that said covered: interval-honest successors beside the legacy trio with the phase grid re-derived by its own method, and a targeted refresh of each materialization hole at service start (partial #3653 — items 16 + 9, Q12 + Q10) #3731:RepairMaterializationHolesAsyncat service start — for every continuous aggregate, the bucket ranges inside its materialized span that the source holds rows for and it never materialized are each refreshed over exactly their bounds, plain first and forced only on a measured remainder, capped at one policy window per aggregate per start; nostart_offsetwidened. The NULL-not-0 below-the-ceiling clause stays open, so the box stays open.) Q10 ruled →RepairMaterializationHolesAsyncDELIVERED in PR Three hourly rollups counted every restart's zero as a sample and a >24 h outage left a two-hour hole under a floor that said covered: interval-honest successors beside the legacy trio with the phase grid re-derived by its own method, and a targeted refresh of each materialization hole at service start (partial #3653 — items 16 + 9, Q12 + Q10) #3731 (targeted refresh of >24 h holes at service start); remaining on this item: the daily-tier calendar'sunique_queries = 0beside fresh raw numbers, and the daily successors (follow-on issue filed today — the daily band must be re-derived first). (NULL-not-0 below the ceiling delivered in PR The Daily-routed calendar prints NULL, not 0, for a day the rollup tier skipped below its ceiling and names it in days_missing[] — the hole scan's two probes per server at day grain, on both daily tools, the viewer's day-detail line and the web calendar (partial #3653 — item 9, A6) #3788:unique_queriesis NULL anddays_missing[]names the day; remaining: the daily successors — Daily band re-derivation is DEFERRED until a daily successor is added (band full 23/23) — the slicing arm landed in #3865 (buckets_per_batch = 1); the storm class was fixed by the WAL ceiling (#3802) #3745)cntr_typenot stored, so gauges are delta'd as activity (a rung); line charts interpolate straight across gaps (only the heatmap NaN-breaks); latch/spinlock snapshot grids show a (0,0) restart row as "delta 0". (PR 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 — three clauses landed; cntr_type rung V132 / v62 landed in PR A falling gauge read as a counter reset because the store did not know it was a gauge: perfmon_stats stores each counter's cntr_type (Darling V132 / Lite v62), the collector writes a gauge as its level with no delta, and every reader classifies by the stored type with the /sec name proxy as the pre-rung fallback (partial #3653 A7) #3708 — gauges written as their level with no delta, every reader classifies by the stored type, the/secproxy is the pre-rung fallback; the ten Wait StatisticsAverage wait time (ms)PERF_AVERAGE_BULK instances are a stated finding, their average needs the base sibling this store does not join)query_store_stats) A8 Rollup rate denominators —QueryStoreTrendRouting.csdivides by gap-to-previous-NON-EMPTY bucket (a quiet hour halves the next hour's true rate). First-point-NULL for differenced series landed in Every delta family now stores the interval its deltas accrued over, and query_stats stores the statement offsets its delta key is made of, so no restart zero reads as a measurement anywhere and the query seed can finally find its keys (#3540, V128 / Lite v61) #3630/Zero is a measurement: health parsers say whether their source was ever seen, a regression with no baseline stays null, and the first point of a differenced trend is no longer a fabricated 0 (#3541 A12) #3642.QUALIFY … LAG > 10000, four escape hatches); perfmon/query baselines don't read the interval they HAVE;PlanCacheAnomalyDetector.IsRealDeltaRowis the repo's one correct restart predicate and is orphaned; UTC-keyed hour-of-week buckets smear under DST. (PR The wait and perfmon baselines stop counting every restart's fabricated zero as a quiet sample: interval-honest successor aggregates replace the legacy pair in place, the provider reads whichever supply covers its window and gates the LAG heuristic on a NULL interval, the legacy pair retires by a self-executing coverage condition, the startup backfill reads coverage off the materialization, and the Daily-routed calendar answers its unmaterialized days from raw (partial #3653: A6 + A10 mechanical half) #3698 — landed the mechanical half: the three CAGG-backed arms and Lite's twins read the stored interval and gateLAG > Nonsample_interval_seconds IS NULL(three-state rule); Lite's BatchRequests heuristic retired outright;IsRealDeltaRowdocumented as the in-memory statement of the rule the SQL tiers take from the collector's stored verdict — not orphaned, not ported;query_stats_baselineneeds no successor (delta_execution_count > 0already excludes unknowable rows); DST: Q6 in PR Hour-of-week baselines key on the target's local clock, not UTC: one shared step-function expression re-keys every arm through the scaffold, three bound clock parameters from server_properties (zone id, offset fallback, UTC when there is no row), the lookup through the same numbers — nothing keyed is stored, so re-bucketing is the next compute (#3653 item 12, Q6) #3749 — buckets key on the target's local clock via time_zone_id, offset fallback; re-bucketing is automatic at the next compute.)query_statshas stored from the start (ViewerDataService.QueryTrends,DarlingTrendReader.QueryDurationTrendSql, LiteGetQueryDurationTrendAsync/GetExecutionCountTrendAsync; pinned "reported, not rewritten" — the LAG-recompute clause landed in PR A quiet hour halved the next hour's Query Store rate, and three trend readers recomputed an interval the store had stored all along: rollup points are rated over their own bucket width, the query-stats trends read sample_interval_seconds three-state on both SKUs, and Compose delta aggregates FILTER out the restart marker (partial #3653: A8, A11's LAG clause, the Cumulative archetype) #3695 for the Storage builder, the viewer and Lite;DarlingTrendReader.QueryDurationTrendSql(alias landed in PR The MCP query-duration trend still divided a restart's zero into 0.00 after the viewer stopped, and two sentences that #3695/#3696 made false: DarlingTrendReader's raw consts become aliases of the Storage builder, the trend trio's descriptions say which points are rated over what, and get_cpu_utilization's Darling note ports Lite's corrected cadences (partial #3653) #3700)); pg statement delta key embeds the datid OID (DROP/CREATE reuse resumes a dead series);cpu_utilization.sample_timeis target-LOCAL against UTC windows;utc_offsetis offset-not-zone across DST; the deprecated Dashboard'scntr_value_per_secondused integer division — the Dashboard READ is fixed (The frozen Dashboard mirrors five of today's honesty fixes in its own idiom: page counts stop posing as totals, one slow wait stops paging, a mute that matched nothing says so, an unbanded collector dot is Unknown, and the per-second counter stops truncating (#3653 Dashboard mirrors) #3658); the Full-edition install scripts still compute it as integer division (install/02_create_tables.sql:916,install/06_ensure_collection_table.sql:788) andinstall/47_create_reporting_views.sql:579-580averages the truncated column — a schema edit on upgrade, its own decision. (Q9: install/02, 06, 47 real division + idempotent upgrade in PR Full-edition perfmon_stats.cntr_value_per_second divides as a number, not an integer; existing installs converge idempotently (#3653 item 13, Q9) #3717) (Q7+Q8 time-honesty rung V134 / Lite v63 in PR cpu_utilization stores each sample's UTC instant beside its server-local stamp and server_properties stores the engine's time-zone id beside its offset (Darling V134 / Lite v63), so UTC-window readers stop deriving an offset that is an hour wrong across DST (#3653 item 13, Q7 + Q8) #3730: sample_time_utc written beside the local stamp and preferred by UTC-window readers; time_zone_id beside the offset, NULL pre-2022) (every clause landed: LAG-recompute A quiet hour halved the next hour's Query Store rate, and three trend readers recomputed an interval the store had stored all along: rollup points are rated over their own bucket width, the query-stats trends read sample_interval_seconds three-state on both SKUs, and Compose delta aggregates FILTER out the restart marker (partial #3653: A8, A11's LAG clause, the Cumulative archetype) #3695/The MCP query-duration trend still divided a restart's zero into 0.00 after the viewer stopped, and two sentences that #3695/#3696 made false: DarlingTrendReader's raw consts become aliases of the Storage builder, the trend trio's descriptions say which points are rated over what, and get_cpu_utilization's Darling note ports Lite's corrected cadences (partial #3653) #3700, datid Identity epochs: a target that restarted, failed over or was re-pointed no longer keeps subtracting from the old instance's counters — detection, forget and a logged, stored discontinuity on both hosts (partial #3653 A5) #3694, Q9 install scripts Full-edition perfmon_stats.cntr_value_per_second divides as a number, not an integer; existing installs converge idempotently (#3653 item 13, Q9) #3717, Q7+Q8 time-honesty rung V134 / Lite v63 cpu_utilization stores each sample's UTC instant beside its server-local stamp and server_properties stores the engine's time-zone id beside its offset (Darling V134 / Lite v63), so UTC-window readers stop deriving an offset that is an hour wrong across DST (#3653 item 13, Q7 + Q8) #3730 — ticked by the coordinator)SUM/AVG/MIN/MAXover a delta column on aCollectorDeltaCalculator.IsDeltaFamilysource carries its ownFILTER (WHERE f.sample_interval_seconds IS DISTINCT FROM 0), Cumulative and the two same-table Delta measures alike; gauges, overlays, ratios,COUNT(*),qs_executionsand the CAGG route untouched) ComposeMeasureCatalogCumulative archetype — all ten families now carrysample_interval_seconds(Every delta family now stores the interval its deltas accrued over, and query_stats stores the statement offsets its delta key is made of, so no restart zero reads as a measurement anywhere and the query seed can finally find its keys (#3540, V128 / Lite v61) #3630), soAVG/MIN/MAX(delta)can emitIS DISTINCT FROM 0uniformly (ComposeCompiler.cs:660).OversizedPlanBacklog.QueryStatsFallbackSqlstays hash-keyed; an exact(plan_handle, sql_handle, offsets)join is possible for V128+ rows if a statement-grain reader appears.LiteDeltaSeederTests.Seed_QueryStats_RestoresKeys…/Seed_PassWindow_Arms…flaked once on an unrelated PR's CI (InRange 118–122, actual 123), green on rerun. One flake is not a pattern; if it recurs the assertion's window is the fix, not the seeder.DeltaFamilyIntervalColumnTests) and 7 (every family seeded —DeltaFamilySeedingCensusTests) are pinned; rules 1–3, 5, 6, 8–10 are not. (PR The measurement contract had ten rules and two tests: the list, numbered once, and censuses for rules 1, 2, 3, 5 and 6 (partial #3653) #3701 — the list written once, numbered, inLite.Tests/MeasurementContractCensusTests' header: 4 and 7 keep their numbers, the six named rules take 1 NULL-not-0 / 2 measured elapsed / 3 epoch / 5 rollups / 6 per-second / 8 gauges, slots 9–10 left empty — the review's enumeration is not on the record. Rules 1, 2, 3, 6 pinned (both SKUs; the Darling half sweeps Storage + Analysis, which theliteCI filter cannot reach); rule 5 rostered against the realTimescaleSupportconstants — the The wait and perfmon baselines stop counting every restart's fabricated zero as a quiet sample: interval-honest successor aggregates replace the legacy pair in place, the provider reads whichever supply covers its window and gates the LAG heuristic on a NULL interval, the legacy pair retires by a self-executing coverage condition, the startup backfill reads coverage off the materialization, and the Daily-routed calendar answers its unmaterialized days from raw (partial #3653: A6 + A10 mechanical half) #3698 successors carry the predicate,query_stats_baseline'sdelta_execution_count > 0is equivalent, andRollupsThatAggregateUnknowableRowsnames the superseded legacy pair plusquery_stats_hourly/procedure_stats_hourly/query_stats_db_hourly, whosesample_count(andmin()on the first two) admit a restart's (0, 0) row — a successor-CAGG lane in The wait and perfmon baselines stop counting every restart's fabricated zero as a quiet sample: interval-honest successor aggregates replace the legacy pair in place, the provider reads whichever supply covers its window and gates the LAG heuristic on a NULL interval, the legacy pair retires by a self-executing coverage condition, the startup backfill reads coverage off the materialization, and the Daily-routed calendar answers its unmaterialized days from raw (partial #3653: A6 + A10 mechanical half) #3698's shape, not a line; rule 8 not census-able untilcntr_typeis stored (the missing column is pinned so the rung reds the row); rules 9–10 not recoverable. Also rostered shrink-only: fifteenELSE 0rate arms (dead text under their WHEREs), twoPgBaselineProviderinterval passthroughs defended by the successor aggregates' predicate, the five families that subtract once before the epoch carrier, andpg_wait_statswith no carrier. Stays open for the rollup roster's emptying, the gauge census once the rung lands, and rule 6's C# payload-key half.) (the fifteen ELSE 0 rate arms retired in PR Fifteen rate arms spelled "unknowable" as 0 behind a guard that happened to hide it: every guarded delta / interval now ends at END, the PostgreSQL trend readers carry the NULL rather than reading it back as 0, and both census rosters are empty (partial #3653) #3707 — roster empty) (rule 5's three hourly rollups superseded by interval-honest successors in PR Three hourly rollups counted every restart's zero as a sample and a >24 h outage left a two-hour hole under a floor that said covered: interval-honest successors beside the legacy trio with the phase grid re-derived by its own method, and a targeted refresh of each materialization hole at service start (partial #3653 — items 16 + 9, Q12 + Q10) #3731 —query_stats_interval_hourly/procedure_stats_interval_hourly/query_stats_db_interval_hourly, appended beside the legacy trio, which stays registered because the indefinite daily tier is hierarchical from it; the roster keeps the five names and now pins every member to a registered successor carrying the predicate; the phase grid re-derived by its method — heaviest :18, window 18 min, watch line 900 s, 4 s above the 896 s ceiling; the daily compression band is full at 23/23) (rule 6's C# payload-key half pinned in PR Rule 6's C# half was a stated bound, not a census: every per-second payload key on both SKUs is now traced to a reader field of its own rate name, a C# quotient over a measured span, a rate helper handed the interval, or an aggregate over a rate — a stored delta under the name is red, and the seven keys whose provenance is another name are rostered with the alias or model quotient each rests on (partial #3653 — item 16, rule 6 C# half) #3784 on both SKUs; rule 5's roster cannot empty by design — every member is pinned to a registered successor since Three hourly rollups counted every restart's zero as a sample and a >24 h outage left a two-hour hole under a floor that said covered: interval-honest successors beside the legacy trio with the phase grid re-derived by its own method, and a targeted refresh of each materialization hole at service start (partial #3653 — items 16 + 9, Q12 + Q10) #3731, the daily successors are Daily band re-derivation is DEFERRED until a daily successor is added (band full 23/23) — the slicing arm landed in #3865 (buckets_per_batch = 1); the storm class was fixed by the WAL ceiling (#3802) #3745's; rule 8 pinned by A falling gauge read as a counter reset because the store did not know it was a gauge: perfmon_stats stores each counter's cntr_type (Darling V132 / Lite v62), the collector writes a gauge as its level with no delta, and every reader classifies by the stored type with the /sec name proxy as the pre-rung fallback (partial #3653 A7) #3708)MCP payload contract (from #3541)
audit_configclaims edition-awareness with zero edition branches and lies for a PG target (no_config_data); "15-second ring buffer" wrong on-prem (the Darling 15-second note ported in PR The MCP query-duration trend still divided a restart's zero into 0.00 after the viewer stopped, and two sentences that #3695/#3696 made false: DarlingTrendReader's raw consts become aliases of the Storage builder, the trend trio's descriptions say which points are rated over what, and get_cpu_utilization's Darling note ports Lite's corrected cadences (partial #3653) #3700); plantop_operatorssilentlyTake(10)+ estimate-basis switch;missing_indexes.impactunlabeled statement-scoped % +paste-ready DDL (collides with the no-missing-index-recs rule)— STRUCK 2026-09-20: no such repo rule exists (maintainer: DDL recommendations are legitimate product output where evidence supports them — RCSI in the analysis engine is the precedent); the misattribution led mute_analysis_finding stops writing the hash as the path and stops registering the same mute twice, remove_server can remove a server that never connected, and eight MCP descriptions stop saying what the code does not do (partial #3653 A15/A16) #3696 to DROPcreate_statementfrommissing_indexes[], which Restore missing_indexes[].create_statement on the plan tools — #3696 dropped it citing a 'no-missing-index-recs rule' that was never made; DDL recommendations are legitimate product output (RCSI precedent) #3805 restores; the AG tool hides its reader's two documented traps;DarlingWebEndpoints.cs:1959describes the duration trend as "percentiles"; 7 truncation dialects / 5 severity vocabularies ("NoData" and "No Data" in one payload) (vocabulary collapse landed in PR One payload said NoData and No Data, eight spellings of four severity words were two bands and six other vocabularies, and the page cut had five names: the daily summary speaks one band token on both SKUs, six inferred cuts become observed truncated + *_returned, and the census classifies every cut key and severity literal by what it is (partial #3653) #3703 — severity one spelling, page cuts one dialect, source-side cuts classified; errors-one-shape awaits Q11; residue on the PR: the PG severity-token ladder is a different vocabulary (band + reason split is a ruling),truncatedcarries the get_query_store_top reports a window it cannot serve: raw query_store_stats is dropped at 4 days #2364 window-floor fact on the trend family,shownfenced by PgTarget tests);StampedUnderCollectionTimetools (get_database_sizes, get_running_jobs, get_server_properties, get_session_stats — read byserver-tabs.jsby the old key); resource-semaphore latest-read ORDER BY drift between SKUs;mute_analysis_findingwritesstory_path = story_path_hashand registers duplicate mutes;remove_servercannot remove a server that never connected (resolves againstservers, notconfig_monitored_servers). (Lite trend descriptions + unrated_note + latch/spinlock parity in PR 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;window_truncatedNOT done — ~15 consumers incl.get_query_store_topinDarlingMcpDataTools.cs, counted on the PR) (errors-one-shape in PR MCP errors take one wire shape: FormatError returns the {status:"error"} envelope on both SKUs (Partial #3653, Q11) #3719) (window_truncatedrename delivered in PR The trend family's window floor is window_truncated on both SKUs, not 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 on both SKUs — the vocabulary census now classifies it; remaining: the two stored-interval spellings, inventoried on the PR) (2026-09-20 A15 lane, verified at source on dev 32096b6: all six remainder clauses were already delivered —audit_configsays what it is and refuses a PostgreSQL target withnot_collected(PostgreSQL targets enter the analysis pipeline: the pass routes by registry engine_kind inside the service, a pg_database_stats coverage witness replaces the tombstone, and every shared switch gains its one pg_ arm so the content lanes can build in parallel (#3542 v1 plumbing) #3665 envelope, mute_analysis_finding stops writing the hash as the path and stops registering the same mute twice, remove_server can remove a server that never connected, and eight MCP descriptions stop saying what the code does not do (partial #3653 A15/A16) #3696 description);top_operatorspublishesoperators_cap/operators_returned/total_operators/truncated/operators_ranked_by, andmissing_indexes[]carriesimpact_basiswith the CREATE INDEX paste REMOVED from the MCP surface (mute_analysis_finding stops writing the hash as the path and stops registering the same mute twice, remove_server can remove a server that never connected, and eight MCP descriptions stop saying what the code does not do (partial #3653 A15/A16) #3696 — the plan viewer still renders the DMV text; whether the MCP surface stays DDL-free is the maintainer's ruling); the AG description and the instructions paragraph name the reader's two traps (mute_analysis_finding stops writing the hash as the path and stops registering the same mute twice, remove_server can remove a server that never connected, and eight MCP descriptions stop saying what the code does not do (partial #3653 A15/A16) #3696); the catalogue line says rates, not percentiles (mute_analysis_finding stops writing the hash as the path and stops registering the same mute twice, remove_server can remove a server that never connected, and eight MCP descriptions stop saying what the code does not do (partial #3653 A15/A16) #3696);server-tabs.jsreads the four stamped tools bycaptured_at(The server page labelled page sums as totals, drew one instant as a trend, and four tools called their capture time by another name: get_pg_database_stats' totals ride the row statement, the memory-grant pair renders window[] beside its newest snapshot, get_pg_io_stats' page count is combinations_returned, and four latest reads stamp captured_at on both SKUs (partial #3653) #3697). PR A15/A16 remainder: the six named drifts were already landed (#3665/#3696/#3697) — this pins the three that had no pin: get_ag_health names its reader's traps, the catalogue's duration-trend line says rates not percentiles, server-tabs.js reads the Stamped tools by captured_at (#3653) #3801 adds the three pins those fixes lacked (AG description + instructions, catalogue line + serializer + whole-catalogue sweep, the JS tile derived from the stamp roster). Rulings left on the face, not work: theget_resource_semaphoreinterval spelling — null-when-unknowable undersample_interval_secondsbesideinterval_known, vs the raw stored 0 onget_file_io_stats/get_perfmon_trendandinterval_secondson the latch/spinlock pair; one fact, three spellings, each described truthfully at its site, inventoried on The trend family's window floor is window_truncated on both SKUs, not 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 — unify or leave; and the PG severity-token ladder's band + reason split (One payload said NoData and No Data, eight spellings of four severity words were two bands and six other vocabularies, and the page cut had five names: the daily summary speaks one band token on both SKUs, six inferred cuts become observed truncated + *_returned, and the census classifies every cut key and severity literal by what it is (partial #3653) #3703).)server-tabs.js:3112-3121tile labels forget_pg_database_stats' page-summed totals (the A7 census's one stated allowance; deleting the allowance is the done signal);get_pg_io_stats.combination_countkept forserver-tabs.js:3173;server-tabs.js:689drawsget_memory_grants.grants[](one instant) as a LINE chart under a window subtitle — chart thewindow[]block Every latest-snapshot MCP read says when it was captured, and no tool accepts a window it does not read (#3541 A10) #3637 added. (delivered in PR The server page labelled page sums as totals, drew one instant as a trend, and four tools called their capture time by another name: get_pg_database_stats' totals ride the row statement, the memory-grant pair renders window[] beside its newest snapshot, get_pg_io_stats' page count is combinations_returned, and four latest reads stamp captured_at on both SKUs (partial #3653) #3697, e6b1ff5:get_pg_database_statspublishes window totals on the statement and the A7 census allowance is DELETED —McpPageContractTestsnow enforces it;combination_count→combinations_returned;get_memory_grants'grants[]/window[]rendered as two tables, window first; the fourStampedUnderCollectionTimetools read bycaptured_at. Ticked by the coordinator 2026-09-20 after verifying each clause at source on dev 050b50e — the box was stale, not the work.)GetCurrent*Async,get_pg_server_configstamped and inDarlingOnlyStamped; the object-stats trio below is NOT done and stays inUnstampedLatestReadsPendingA10) A10 residual —McpLatestSnapshotStampTests' reader-call regex does not matchGetCurrentConfigAsync, soget_pg_server_config(an unstamped latest-snapshot read) sits OUTSIDE Every latest-snapshot MCP read says when it was captured, and no tool accepts a window it does not read (#3541 A10) #3637's census; a sibling lane added aDarlingOnlyStampedroster allowance but did not widen the regex — widen it and stamp the tool. Object-stats trio unstamped (get_index_usage,get_object_locking,get_table_index_sizes; per-database daily snapshot → per-row stamp), held in the census asUnstampedLatestReadsPendingA10(shrink-only).LocalDataService.LatchSpinlock.cs:129/261LIMIT 20literal caps behind get_latch_stats/get_spinlock_stats (also the viewer grid).ViewerDataService.DailySummary.csreads the same SQL and now receivessignal_sources_present; its port isHasData = DailySummaryRetention.StateFor(...) == Collectedplus the horizon read.DailyHealthBandCalculator.Describesays "No data collected." for anyHasData = false— wording for thepurged/past_horizonstates is Blocking and CPU bands are rates over the window they were measured in, tiered from 14 days of fleet data, so a week of blocking and an hour of blocking stop getting the same colour (#3539 A2/A3/A8d) #3596's file. Per-signal nulling of purged counts (the honest end state;past_horizondiscloses the ambiguity instead) is a lane of its own.ViewerDataService.QueryTrends.csQueryDurationTrendSql/QueryStoreDurationTrendSql/ execution-count copies still carryELSE 0(Zero is a measurement: health parsers say whether their source was ever seen, a regression with no baseline stays null, and the first point of a differenced trend is no longer a fabricated 0 (#3541 A12) #3642 fixed the MCP readers; the procedure copy went in Every delta family now stores the interval its deltas accrued over, and query_stats stores the statement offsets its delta key is made of, so no restart zero reads as a measurement anywhere and the query seed can finally find its keys (#3540, V128 / Lite v61) #3630);PvsTrendPoint.PvsSizeMbcoerces NULL to 0 on the top-5 trend series (both SKUs, rare) — Lite + the Darling Viewer chart in The Darling viewer's trend charts and calendar tell the same truth the MCP tools learned today: routed by retention tier and saying so, no fabricated first point, and a purged day is grey, not green (#3653 viewer ports) #3666; the Darling MCP reader (DarlingPvsReader,get_pvs_statstrend arm) in PR Darling's get_pvs_stats trend drew an unmeasured pass as 0 MB: the point is now null with pvs_measured beside it, the same two keys Lite's twin has carried since #3666, and a measured zero is still a zero (#3653) #3682 withpvs_measuredper point.DurationTrendRouting) had the same raw-only read the MCP trio had before The duration-trend trio routes by retention tier and discloses its source like get_query_trend already does, so a 7-day request no longer returns 4 days labelled quiet (#3541 A2) #3590 (ViewerDataService.QueryTrends.cs:239-246); a 7-day chart on a TimescaleDB store plots 4 days. Port =DarlingTrendReader.*HourlySql+ResolveTier.DarlingTrendReader's ladder/hourly-SQL members become ALIASES ofDurationTrendRouting(the The Darling viewer's trend charts and calendar tell the same truth the MCP tools learned today: routed by retention tier and saying so, no fabricated first point, and a purged day is grey, not green (#3653 viewer ports) #3666 equality pin → an identity pin over the declarations; zero behaviour, proven by a before/after reflection dump of the built dll), and the Query Store duration chart — the one Performance Trends chart The Darling viewer's trend charts and calendar tell the same truth the MCP tools learned today: routed by retention tier and saying so, no fabricated first point, and a purged day is grey, not green (#3653 viewer ports) #3666 left untitled — discloses its route word and the corrected rollup's unserved floor (QueryStoreTrendRouting.SourceWord/UnservedBefore, one rule the MCP tool and the chart both read). Lite needs no twin:v_query_store_statsunions live + parquet at one grain, no floor to disclose.*Readerfield andLite/Services/*.cs) ORDER BY integer-expression sweep —ORDER BY 3 + 4 DESCfolds to a constant in PostgreSQL (MCP percents name their denominator: shares are of the window or say they are of the page, so a three-row page stops summing to 100% of everything (#3541 A7) #3613 foundget_pg_kernel_statshad never been ranked); one fact inDarlingPgReadSqlParsesLiveTestsgrepping everyDarlingPg*Readerconst forORDER BY\s+\d+\s*[-+*/]\s*\d+closes the class.PerformanceMonitor.Common/Mcp/McpHelpers.csis the code-side home. (PR Three PostgreSQL pages observe truncation off limit + 1 through one shared helper, the census sweeps the inference class instead of a roster, and four payload rules become facts and inventories, so three tools stop guessing truncation from a full page and four of the eleven payload rules stop being sentences (partial #3653) #3699 —McpPayloadContractCensusTests+McpHelpers.BoundPagelanded: refuse what you cannot honor pinned (every bounded parameter reaches a shared validator or one of five rostered inlinedays_backrefusals; no parameter is clamped; the timestamp arm's two generalTryParsesites rostered), errors one shape pinned as "every catch returns through a shared shape" with the one ad-hoc site rostered — but the two shared shapes (a bare string and a JSON envelope, 21 PG files on the envelope) are an INVENTORY for the vocabulary lane, the rule itself is not met; name = truth's two greps pinned as inventories (10total_* = .Countwhole-set sites; latest-anchored readers project their anchor bar the two known A10 residuals) and the rest stated as review-checklist; one vocabulary rostered shrink-only (14 truncation-key dialects, 8 severity-word spellings in three casings, theNoData/No Datapair by named instance, 7 statement-terminal literalLIMITs all Lite viewer-side) for the wave-3 vocabulary lane; writes report what happened already pinned per tool by MCP write tools report what happened: every server you add lands in a counter, a partial name cannot delete the wrong server, an omitted field is never a clear, and a mute that matched nothing says so (#3541 A14) #3615, a cross-write census deferred behind the envelope lane's mute rewrite. The page-inference sweep now covers every tool body on both SKUs.) (errors one shape: every failure is the JSON envelope through FormatError, PR MCP errors take one wire shape: FormatError returns the {status:"error"} envelope on both SKUs (Partial #3653, Q11) #3719)Frozen-twin mirror batch (
deprecated/Dashboard, bug-fix support — one lane, smallest faithful diff each)Mcp/McpBlockingTools.cs:68,133,McpAlertTools.cs:104—total_events/total_deadlocks/total_alertsare page counts (MCP pages now say what bounded them: caps bind to the caller's limit, truncation is detected not inferred, and no page count is called a total (#3541 A3) #3594's class; caps live in the Dashboard's own readers).MainWindow.AlertEngine.cs:477+Services/DatabaseService.NocHealth.cs:877— the avg-ms poison-wait loop over live SQL (SQL Server's Poison Wait alert measures accumulated starvation over a ten-minute window like its PostgreSQL twin, so one slow wait no longer pages and a THREADPOOL storm no longer sleeps (#3539 A4) #3593's class).Mcp/McpAnalysisTools.cs:~429—mute_analysis_findingreturns"muted"for any hash (MCP write tools report what happened: every server you add lands in a counter, a partial name cannot delete the wrong server, an omitted field is never a clear, and a mute that matched nothing says so (#3541 A14) #3615's class); compare keeps the ±0.1 band (compare_analysis bands each delta by the server's own dispersion and folds one cause into one row, so same-hour-yesterday noise stops reading as a verdict (#3538 A3) #3634's class); noconfidence_basis(Story confidence measures corroboration instead of path length, and the nightly rebuild's three cards become one incident that names the job (#3538 A6/A9) #3632).Models/ServerHealthStatus.cs:510— ownCollectorSeveritywith(0,0) → Healthy(A server nothing has banded yet is Unknown, not Healthy, and the alert-history grids show the severity the alert actually fired at instead of the colour its name implies (#3539 A6/A8e) #3635's class).Analysis/SqlServerFactCollector.Waits.cs:54,121,178— nominal-window divisor (Analysis facts divide by the time the collector actually observed, and a window with a hole in it says so, so collector downtime no longer reads as improvement (#3538 A2) #3592's class) — declined: needs a T-SQL coverage witness plus its own service/MCP surfaces, not a mirror.(done in The frozen Dashboard mirrors five of today's honesty fixes in its own idiom: page counts stop posing as totals, one slow wait stops paging, a mute that matched nothing says so, an unbanded collector dot is Unknown, and the per-second counter stops truncating (#3653 Dashboard mirrors) #3658).Analysis/SqlServerAnomalyDetector.cs:38,74— old HAMMERDB markersBatch landed in #3658 (c7ca1ad): items 1–4 and the anomaly-detector markers; the Waits.cs divisor stays declined and open as stated.
compare_analysis±0.1 band /confidence_basisdeliberately not mirrored (semantics upgrades, not bug fixes on a frozen twin).Not here