Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
452 commits
Select commit Hold shift + click to select a range
44fd728
Merge pull request #2198 from erikdarlingdata/fix/2187-installer-user…
erikdarlingdata Aug 11, 2026
3aff9b7
Never learn a transient state as a database's expected state (#2189)
erikdarlingdata Aug 11, 2026
1465af5
darling: window the block and WAL figures in get_pg_top_queries
erikdarlingdata Aug 11, 2026
e659dae
Merge remote-tracking branch 'origin/dev' into fix/2189-restoring-bas…
erikdarlingdata Aug 11, 2026
9558744
Merge remote-tracking branch 'origin/dev' into fix/2186-bootstrap-err…
erikdarlingdata Aug 11, 2026
e338903
Port the prune to Lite, and require the snapshot to be current (#2188)
erikdarlingdata Aug 11, 2026
a1916a9
Keep {ExitCode} numeric for structured sinks; SkipUnless over vacuous…
erikdarlingdata Aug 11, 2026
d7e8a52
Merge remote-tracking branch 'origin/dev' into fix/2188-planwm-prune
erikdarlingdata Aug 11, 2026
5d47b8d
darling: fix timestamptz handling, and gate autovacuum off standbys
erikdarlingdata Aug 11, 2026
fbd9a93
Merge pull request #2202 from erikdarlingdata/fix/2189-restoring-base…
erikdarlingdata Aug 11, 2026
20669f5
Merge remote-tracking branch 'origin/dev' into fix/2186-bootstrap-err…
erikdarlingdata Aug 11, 2026
aa736f7
darling: pg_io_stats and get_pg_io_stats — I/O by who, what and why
erikdarlingdata Aug 11, 2026
897196a
Merge remote-tracking branch 'origin/dev' into fix/2188-planwm-prune
erikdarlingdata Aug 11, 2026
a8a98bd
darling: classify PostgreSQL collector faults instead of logging ERRO…
erikdarlingdata Aug 11, 2026
cdb30e5
Close the drift guard's last two holes (#2188)
erikdarlingdata Aug 11, 2026
cdaabad
Merge pull request #2194 from erikdarlingdata/fix/2186-bootstrap-erro…
erikdarlingdata Aug 11, 2026
f08b264
Merge remote-tracking branch 'origin/dev' into fix/2188-planwm-prune
erikdarlingdata Aug 11, 2026
d6d1d16
Lite gets the persisted alerted-state memory (#2203, the #2166 Lite h…
erikdarlingdata Aug 11, 2026
5f5665f
Give Lite's anti-join the same NULL semantics as Darling's (#2188)
erikdarlingdata Aug 11, 2026
2e38447
Merge pull request #2204 from erikdarlingdata/lite-alerted-state-memo…
erikdarlingdata Aug 11, 2026
ab02419
Merge pull request #2195 from erikdarlingdata/fix/2188-planwm-prune
erikdarlingdata Aug 11, 2026
a6376aa
docs: scope PostgreSQL alerting, and name the decision it needs
erikdarlingdata Aug 11, 2026
d8193a0
Assert compression-job health from the settled snapshot (#2206)
erikdarlingdata Aug 11, 2026
3b3e864
darling: alert on the PostgreSQL outage predictors (Option B)
erikdarlingdata Aug 11, 2026
b688532
Say why the health assertion is tautological (#2206)
erikdarlingdata Aug 11, 2026
cc31b11
docs: design note for the PostgreSQL blocking-chain collector
erikdarlingdata Aug 11, 2026
a6e1326
Merge pull request #2207 from erikdarlingdata/timescale-selfheal-sett…
erikdarlingdata Aug 11, 2026
e42bfdc
Decide "first run" vs "bootstrap already failed" from the store's own…
erikdarlingdata Aug 11, 2026
2f9d0fe
darling: report a PostgreSQL target honestly at pre-flight
erikdarlingdata Aug 11, 2026
939d38c
Carry the decoded-in-words clause into the Viewer's copy of the messa…
erikdarlingdata Aug 11, 2026
9028999
Merge pull request #2209 from erikdarlingdata/fix/2197-credential-mis…
erikdarlingdata Aug 11, 2026
6c6fb9d
Plan-fetch candidate window and cut-safe watermark advance (#2210)
erikdarlingdata Aug 11, 2026
8d3f13d
Hand the ordering verdict back with the watermark (#2210)
erikdarlingdata Aug 11, 2026
f18b481
darling: let a PostgreSQL target survive its own registration
erikdarlingdata Aug 11, 2026
e01b281
Query Store plan map: liveness, prune, and the margin ordering (#2210)
erikdarlingdata Aug 12, 2026
d0fb02a
Pin the margin ordering the invariant claimed a test would catch (#2210)
erikdarlingdata Aug 12, 2026
bbc3019
Review round: clamped contract, dead guard, doc contradiction (#2210)
erikdarlingdata Aug 12, 2026
94ce2f4
The plan_id-ordered plan fetch query (#2210)
erikdarlingdata Aug 12, 2026
c27b04a
Admit a plan on the total BEFORE it, so one oversized plan cannot sta…
erikdarlingdata Aug 12, 2026
eb75771
Merge origin/dev, and renumber the PostgreSQL rungs off a collision
erikdarlingdata Aug 12, 2026
98517e0
darling: teach the viewer's store-schema probe about V68
erikdarlingdata Aug 12, 2026
e03d37a
Database-state writes take the WRITE lock (#2208)
erikdarlingdata Aug 12, 2026
6e9ec7f
darling: restore a doc block my insertion displaced, and log the rele…
erikdarlingdata Aug 12, 2026
0017bf7
docs: state what the runbook has and has not proven, by layer
erikdarlingdata Aug 12, 2026
c7be750
Merge pull request #2212 from erikdarlingdata/lite-dbstate-write-lock…
erikdarlingdata Aug 12, 2026
1111058
Pin that the operator write paths take the write lock (#2208)
erikdarlingdata Aug 12, 2026
8e6b89e
darling: stop the slot table shadowing pg_catalog, and fix four CI reds
erikdarlingdata Aug 12, 2026
8aef3cf
Use the lock's recursion policy, not its timeout (#2208)
erikdarlingdata Aug 12, 2026
2f7d7f4
Merge pull request #2214 from erikdarlingdata/lite-write-lock-pin-2208
erikdarlingdata Aug 12, 2026
650c078
Convert once inside the window — measured, not reasoned (#2210)
erikdarlingdata Aug 12, 2026
63b3f1a
darling+lite: classify the two new config columns, and keep Lite's st…
erikdarlingdata Aug 12, 2026
f4dfe26
darling: grant the two new config columns in provision-roles.sql too
erikdarlingdata Aug 12, 2026
bd45370
A NULL query_plan ships as zero bytes instead of stalling (#2210)
erikdarlingdata Aug 12, 2026
633b655
TouchSql returns the reset signal in the same round trip (#2210)
erikdarlingdata Aug 12, 2026
3433605
OPTION(RECOMPILE) and a positive-budget guard on the fetch (#2210)
erikdarlingdata Aug 12, 2026
8edb6f7
plan_hash on the map, and floor the estimate during catch-up (#2210)
erikdarlingdata Aug 12, 2026
e58d6c4
The re-verify cursor, and RefreshAfter stops meaning expiry (#2210)
erikdarlingdata Aug 12, 2026
a3733c1
Pin that a full cursor sweep never moves the watermark (#2210)
erikdarlingdata Aug 12, 2026
dcdfada
The reset arm returns facts, not a verdict; guard candidatePlans (#2210)
erikdarlingdata Aug 12, 2026
da54e81
V69: the Query Store plan map table (#2210)
erikdarlingdata Aug 12, 2026
0e2b98a
Wire the map prune into retention, and pin the both-orders race (#2210)
erikdarlingdata Aug 12, 2026
f39a963
CHANGELOG: the plan-XML cutover and its transition window (#2210)
erikdarlingdata Aug 12, 2026
9a0ef14
A Naive() helper for the map's timestamp binds (#2210 / #1969)
erikdarlingdata Aug 12, 2026
0040984
The map+dim write path for fetched plans (#2210)
erikdarlingdata Aug 12, 2026
dcad861
Route the plan fetch through the runner (#2210)
erikdarlingdata Aug 12, 2026
b5011f0
#2210: drop the runtime-query plan-XML shape; retire the tests that p…
erikdarlingdata Aug 12, 2026
5818dac
Renumber the map rung to max(dev)+1 and bump SchemaVersion (#2210)
erikdarlingdata Aug 12, 2026
e85cc2d
Point the hardcoded schema-version pins at 61 (#2210)
erikdarlingdata Aug 12, 2026
ba760d2
darling: give every PostgreSQL collector a PostgreSQL connection, and…
erikdarlingdata Aug 12, 2026
7137312
darling: make the xmin horizon see idle-in-transaction, and give the …
erikdarlingdata Aug 12, 2026
6d2e3af
darling: stop the Aurora-only collectors fake-succeeding, and un-reno…
erikdarlingdata Aug 12, 2026
0aede48
Teach the viewer's schema probe about V61 (#2210)
erikdarlingdata Aug 12, 2026
7f270f2
darling: return a doc block my insertion displaced, and make the scan…
erikdarlingdata Aug 12, 2026
b7658e7
tests: give two assertions I weakened their teeth back
erikdarlingdata Aug 12, 2026
e8b5601
darling: make the pg_stat_io NULL comment true instead of aspirational
erikdarlingdata Aug 12, 2026
b1ef0cd
darling: grade MultiXacts in the MCP severity, rank insert-only table…
erikdarlingdata Aug 12, 2026
91fe56d
darling: clear five MINOR review items
erikdarlingdata Aug 12, 2026
cb9adbf
darling: count wraparound headroom to the stop point, and stop hiding…
erikdarlingdata Aug 12, 2026
c35706c
Fix the four ladder-pin failures: invariant form for newest-rung, 44t…
erikdarlingdata Aug 12, 2026
c517117
darling: close the last two MEDIUM items
erikdarlingdata Aug 12, 2026
8183793
tests: assert the autovacuum ranking by structure, not by an exact SQ…
erikdarlingdata Aug 12, 2026
d6a9475
Give the WAM broker a parent window handle so Entra MFA works (#2184)
erikdarlingdata Aug 12, 2026
ec03916
Address review: log the handle-resolution fallback instead of failing…
erikdarlingdata Aug 12, 2026
caca641
Merge pull request #2217 from erikdarlingdata/feature/2184-entra-wam-…
erikdarlingdata Aug 12, 2026
ad757a3
darling: stop running SQL-Server-shaped analysis against PostgreSQL t…
erikdarlingdata Aug 12, 2026
bfd8725
Merge remote-tracking branch 'origin/dev' into feat/postgres-target-c…
erikdarlingdata Aug 12, 2026
205b9f5
An incident can carry a total, not just a window count (#2216)
erikdarlingdata Aug 12, 2026
7defad6
The occurrence accumulator (#2216)
erikdarlingdata Aug 12, 2026
7c1ffb6
Persist the counters in both stores (#2216): Darling V61, Lite v54
erikdarlingdata Aug 12, 2026
83788ba
Wire both count gates to the accumulator (#2216)
erikdarlingdata Aug 12, 2026
8aa51e7
Cover the cases where the gauge is uninformative (#2216)
erikdarlingdata Aug 12, 2026
abb17d3
CHANGELOG: the occurrence total and its honest bound (#2216)
erikdarlingdata Aug 12, 2026
6d942dd
Observe occurrences on every sweep, not only on deliveries (#2216)
erikdarlingdata Aug 12, 2026
b4da556
Store-level plan XML codec knob: gzip (default) or plain text (#2171)
erikdarlingdata Aug 12, 2026
f361dad
Add the pg_blocking collector: PostgreSQL blocking chains as an edge …
erikdarlingdata Aug 12, 2026
f698a06
Fix the coverage-ratchet failure: don't name a collector table in vie…
erikdarlingdata Aug 12, 2026
c3621ca
Pin the ladder dense above the historical V45 hole (#2226)
erikdarlingdata Aug 12, 2026
a871b3c
Merge pull request #2227 from erikdarlingdata/feature/2226-ladder-den…
erikdarlingdata Aug 12, 2026
d71c580
Merge branch 'dev' into feat/2216-incident-occurrence-counter
erikdarlingdata Aug 12, 2026
51340fb
Merge pull request #2221 from erikdarlingdata/feat/2216-incident-occu…
erikdarlingdata Aug 12, 2026
810edbb
Merge origin/dev (V61 incident-occurrence-counters) under V62
erikdarlingdata Aug 12, 2026
89f486e
Normalize the codec knob at the seed write, not just the read (#2171)
erikdarlingdata Aug 12, 2026
6054cbb
Merge pull request #2224 from erikdarlingdata/fix/2171-plan-xml-compr…
erikdarlingdata Aug 12, 2026
22a0dde
Round-2 blockers 1-3: gate the last three engine-blind doors, fix the…
erikdarlingdata Aug 12, 2026
5e9c6db
Blocker 4: merge dev (V61+V62) and renumber the ladder to V63-V70 (#2…
erikdarlingdata Aug 12, 2026
bdd453e
Round-3 review fixes: mirror the EnsureAllAsync self-gate, recursive …
erikdarlingdata Aug 12, 2026
922d99d
Merge pull request #2213 from erikdarlingdata/feat/postgres-target-co…
erikdarlingdata Aug 13, 2026
da4f65e
Merge dev: renumber the blocking rung V69 -> V71 after #2213's own re…
erikdarlingdata Aug 13, 2026
cf8da33
Bind the blocking reader's window as naive UTC, not Kind=Utc
erikdarlingdata Aug 13, 2026
e667c02
Review round: four defects in the blocking slice, all silent-wrong-an…
erikdarlingdata Aug 13, 2026
5fbf6ba
Second review round: seven more findings, including a real invisibili…
erikdarlingdata Aug 13, 2026
5bdb8ef
Assert the invariant, not the scenarios: no stored edge may be invisible
erikdarlingdata Aug 13, 2026
2b4372d
Merge pull request #2225 from erikdarlingdata/feat/postgres-blocking-…
erikdarlingdata Aug 13, 2026
693d65c
Execution coverage for the blocking reader's ordinals (#2225 review, …
erikdarlingdata Aug 13, 2026
2e7c1f5
Reindent the extracted mapper argument lists (review nit)
erikdarlingdata Aug 13, 2026
8752c4e
Lite's query_store prune names the retired keys, like Darling's (#2205)
erikdarlingdata Aug 13, 2026
9bed451
Correct the comment: Last() finds the final SELECT, not indentation (…
erikdarlingdata Aug 13, 2026
7dfa9c5
Merge pull request #2236 from erikdarlingdata/fix/2205-lite-prune-nam…
erikdarlingdata Aug 13, 2026
4096407
Merge pull request #2232 from erikdarlingdata/fix/pg-blocking-reader-…
erikdarlingdata Aug 13, 2026
8dbca64
Behavioral coverage for the analyze_now PostgreSQL engine gate (#2230)
erikdarlingdata Aug 13, 2026
5618605
Fix the test's identity derivation: StorageName comes from Host, not …
erikdarlingdata Aug 13, 2026
01ba847
Clear the baseline analyzer warnings so a zero-warning gate is readab…
erikdarlingdata Aug 13, 2026
23d88f5
Invert a bogus assertion: the engine message QUOTES 'still collecting'
erikdarlingdata Aug 13, 2026
c611456
Merge pull request #2238 from erikdarlingdata/fix/2193-baseline-analy…
erikdarlingdata Aug 13, 2026
1ea2889
#2193 follow-up: nest the para inside <summary>, drop a dead using
erikdarlingdata Aug 13, 2026
499c5b6
Merge pull request #2239 from erikdarlingdata/fix/2193-followup-doc-a…
erikdarlingdata Aug 13, 2026
06df897
Route the test's teardown through LiveStoreCleanup (#1902 ratchet)
erikdarlingdata Aug 13, 2026
a076f49
Merge pull request #2237 from erikdarlingdata/test/2230-pg-engine-gat…
erikdarlingdata Aug 13, 2026
7bfdbbd
Fail the review workflow when it posts nothing (#2229)
erikdarlingdata Aug 13, 2026
e1c13fa
Tell "never ran" apart from "reviewed silently" in the review guard
erikdarlingdata Aug 13, 2026
87069e3
Guard the review from outside, and name what #2229 actually is
erikdarlingdata Aug 13, 2026
6fb3779
Scope the guard's count to the reviewer, and don't forget a run once …
erikdarlingdata Aug 13, 2026
f7d5dfc
Record why the guard needs no issues: scope
erikdarlingdata Aug 13, 2026
297213c
Merge pull request #2240 from erikdarlingdata/fix/2229-review-posted-…
erikdarlingdata Aug 13, 2026
495f3d3
Stop the delta gap guard from fabricating zeros on ordinary sweeps
erikdarlingdata Aug 13, 2026
6df9906
Point the parity pins at the shared policy instead of the number
erikdarlingdata Aug 13, 2026
d4fe892
Fix a real bug in my own fix: the interval is not additive across ins…
erikdarlingdata Aug 13, 2026
c48efa6
Carry the interval into the Viewer's copy too, so the parity claim st…
erikdarlingdata Aug 13, 2026
bfd4b9e
Give Lite the MAX-vs-SUM pin its Darling twins got, and stop overstat…
erikdarlingdata Aug 13, 2026
7c8c9b9
Merge pull request #2244 from erikdarlingdata/fix/2233-2234-delta-gap…
erikdarlingdata Aug 13, 2026
2122ccd
Cover the reconcile engine gate behaviorally, no seam required after all
erikdarlingdata Aug 13, 2026
3b467ac
Blank line between the new helper and the next member
erikdarlingdata Aug 13, 2026
96d712f
Merge pull request #2245 from erikdarlingdata/test/2230-reconcile-gat…
erikdarlingdata Aug 13, 2026
466b157
Gate tempdb_stats off Azure SQL Database: it can only ever fail there
erikdarlingdata Aug 14, 2026
556ffda
Blank line after the new pin
erikdarlingdata Aug 14, 2026
6f24be2
Merge pull request #2247 from erikdarlingdata/fix/tempdb-stats-azure-…
erikdarlingdata Aug 14, 2026
2c95e26
#2201 test FIRST, expected RED: the mapped-drive probe fails open wit…
erikdarlingdata Aug 14, 2026
31f45b3
#2201 fix: consult PSDrive DisplayRoot when WMI cannot answer
erikdarlingdata Aug 14, 2026
12716ed
Restore the blank line before RunWindowsPowerShell's doc comment
erikdarlingdata Aug 14, 2026
547cfde
Merge pull request #2248 from erikdarlingdata/fix/2201-mapped-drive-w…
erikdarlingdata Aug 14, 2026
878baa1
Treat WMI DriveType 0 as unknown rather than local (#2248 review catch)
erikdarlingdata Aug 14, 2026
47b7c71
Merge pull request #2249 from erikdarlingdata/fix/2201-followup-unkno…
erikdarlingdata Aug 14, 2026
00063b7
Six cases, not five
erikdarlingdata Aug 14, 2026
b8afbd4
Merge pull request #2250 from erikdarlingdata/fix/stale-case-count
erikdarlingdata Aug 14, 2026
8ea48e5
Base the FinOps provisioning verdict on signals that mean something
erikdarlingdata Aug 14, 2026
ed99cab
Fix the two copies review found, and stop the UI inventing a cause
erikdarlingdata Aug 14, 2026
8b66837
Pin Lite's FinOps reads too, so the same mistake cannot fail silently…
erikdarlingdata Aug 14, 2026
80de904
Apply the drift guard to all three verdict reads, not just the one th…
erikdarlingdata Aug 14, 2026
e95b49a
Merge pull request #2251 from erikdarlingdata/fix/2246-provisioning-v…
erikdarlingdata Aug 14, 2026
5475ab3
Drop the dead memory_ratio from the two inventory reads
erikdarlingdata Aug 14, 2026
062f9f7
Merge pull request #2253 from erikdarlingdata/fix/2246-drop-dead-memo…
erikdarlingdata Aug 14, 2026
67ce64f
Name the servers darling.json lists that the store does not have (#2254)
erikdarlingdata Aug 14, 2026
5349dd8
CHANGELOG entry for #2254
erikdarlingdata Aug 14, 2026
c520a83
Reconcile rather than accuse: the file/store difference has two causes
erikdarlingdata Aug 14, 2026
7d6ce49
Merge pull request #2257 from erikdarlingdata/fix/2254-warn-file-only…
erikdarlingdata Aug 14, 2026
78ff8a1
--add-server: a headless host can finally register a monitored server…
erikdarlingdata Aug 14, 2026
1c9e558
Put the verb where it belongs, and move the count pin with it
erikdarlingdata Aug 14, 2026
4082242
Put the managed refusal where Windows is provable, and document the p…
erikdarlingdata Aug 14, 2026
8e01b13
A store failure is not a parse failure
erikdarlingdata Aug 14, 2026
90fab36
Merge pull request #2259 from erikdarlingdata/feat/2256-add-server-verb
erikdarlingdata Aug 14, 2026
8cd6db9
CHANGELOG: backfill the five shipped changes that had no entry
erikdarlingdata Aug 14, 2026
0d7f2fe
Cover the snapshot_now engine gate behaviorally — the last of #2230's…
erikdarlingdata Aug 14, 2026
3f36448
Drop the extra blank line the Changed insert added
erikdarlingdata Aug 14, 2026
0ff03f5
A clean review is not a swallowed one — stop failing on silence (#2229)
erikdarlingdata Aug 14, 2026
c2ad1ca
Take the suite's unreachable-endpoint idiom instead of a .invalid hos…
erikdarlingdata Aug 14, 2026
75d8174
Merge pull request #2262 from erikdarlingdata/fix/2229-guard-clean-re…
erikdarlingdata Aug 14, 2026
737dcfe
Merge dev: pick up the review-guard fix (#2262) so verify re-runs wit…
erikdarlingdata Aug 14, 2026
f1587ea
Merge dev: pick up the review-guard fix (#2262) so verify re-runs wit…
erikdarlingdata Aug 14, 2026
094b904
Merge pull request #2260 from erikdarlingdata/docs/changelog-backfill…
erikdarlingdata Aug 14, 2026
aad7986
Merge pull request #2261 from erikdarlingdata/test/2230-snapshot-now-…
erikdarlingdata Aug 14, 2026
329d987
Read server_id from the store instead of re-deriving it (#2218 step 1)
erikdarlingdata Aug 14, 2026
fa4a210
Teach the two guards that caught this, rather than working around them
erikdarlingdata Aug 14, 2026
b280230
Wrap the doc line my edit ran together
erikdarlingdata Aug 14, 2026
c4b2ad5
MCP live plan fetch must key on the registry, not darling.json
erikdarlingdata Aug 14, 2026
b68d90b
Merge pull request #2263 from erikdarlingdata/feat/2218-server-id-fro…
erikdarlingdata Aug 14, 2026
cabe14c
Bound one database's collection by wall clock, so it cannot starve th…
erikdarlingdata Aug 14, 2026
9479357
Bound the watermark refresh too, as the contract already promised
erikdarlingdata Aug 14, 2026
d9f5960
Order the OOM rethrow ahead of the budget arm in both per-database loops
erikdarlingdata Aug 14, 2026
53d6a45
Merge pull request #2264 from erikdarlingdata/fix/2150-per-database-w…
erikdarlingdata Aug 14, 2026
13af311
Merge dev into #2210: renumber the rung to V72, resolve the version l…
erikdarlingdata Aug 14, 2026
7067907
Attach the V72 doc block to its own const, not to V71's
erikdarlingdata Aug 14, 2026
53878d0
Merge pull request #2211 from erikdarlingdata/qs-plan-fetch-policy-2210
erikdarlingdata Aug 14, 2026
cd83354
Scope an Azure SQL DB sweep to the database it registered (#2220)
erikdarlingdata Aug 14, 2026
c4c844a
Do not throttle the master probe on the path that has nothing to fall…
erikdarlingdata Aug 14, 2026
4577390
Drop the predicate I did not use, and refresh both stale summaries
erikdarlingdata Aug 14, 2026
aac1c26
Restore the clause my Lite customization dropped
erikdarlingdata Aug 14, 2026
361e8c3
Merge pull request #2265 from erikdarlingdata/fix/2220-scope-azure-sw…
erikdarlingdata Aug 14, 2026
de8e9c4
Seed inside the retention horizon these tests arm (#2223)
erikdarlingdata Aug 14, 2026
b1e680d
Use the TimeSpan the product already exposes, not a re-parse
erikdarlingdata Aug 14, 2026
dc8c4c7
Merge pull request #2267 from erikdarlingdata/fix/2223-seed-inside-th…
erikdarlingdata Aug 14, 2026
f970210
CONTRIBUTING.md sent contributors to a file that is deliberately priv…
erikdarlingdata Aug 14, 2026
7c8dd72
Prune per-database state on Azure SQL DB (#2191)
erikdarlingdata Aug 14, 2026
27aa303
Drop the one rule I could not ground in the code
erikdarlingdata Aug 14, 2026
3d20045
search_path is a PostgreSQL rule, not a T-SQL one
erikdarlingdata Aug 14, 2026
148caed
Cover the Azure prune's actual SQL execution, on Lite (#2191)
erikdarlingdata Aug 14, 2026
4ace994
Merge pull request #2268 from erikdarlingdata/docs/2243-contributing-…
erikdarlingdata Aug 14, 2026
83bec28
Merge pull request #2269 from erikdarlingdata/fix/2191-prune-azure-pe…
erikdarlingdata Aug 14, 2026
e02c2f5
Name which binary could not load when initdb dies in the loader (#218…
erikdarlingdata Aug 14, 2026
f452a4a
Let the incident readers filter by an alert's Dedup Key (#2159) (#2271)
erikdarlingdata Aug 14, 2026
196c7b8
Explain a DPAPI decrypt failure instead of leaking the raw error (#22…
erikdarlingdata Aug 14, 2026
020758f
Serialize the Query Store tick and backfill per server (#2165) (#2272)
erikdarlingdata Aug 14, 2026
e0dd28f
Roll proc-hosted dynamic SQL up to its parent in top-queries (#2235) …
erikdarlingdata Aug 15, 2026
cb2d109
Stop asserting a best-effort maintenance cycle always runs (#2266 ite…
erikdarlingdata Aug 15, 2026
406c8ab
An edit keeps its server identity, so history stays attached (#2158) …
erikdarlingdata Aug 15, 2026
7c395eb
Trip when a registration is connected to a database it does not name …
erikdarlingdata Aug 15, 2026
5f7f11d
Carry engine and port in the server identity, re-keying nothing (#221…
erikdarlingdata Aug 15, 2026
c1c5a75
Report a skipped database-state maintenance cycle (#2266 follow-on) (…
erikdarlingdata Aug 15, 2026
5d4f656
Warn when a SQL-auth password may not be decryptable by the service (…
erikdarlingdata Aug 15, 2026
b31c943
Refuse a registration that lands on an already-monitored database (#2…
erikdarlingdata Aug 15, 2026
0b6bc3a
Store PostgreSQL statement text so top-queries is readable (#2219) (#…
erikdarlingdata Aug 15, 2026
e333802
Tell "never registered" from "deliberately removed" (#2258) (#2285)
erikdarlingdata Aug 15, 2026
dffbff5
Report what the compression job did, not just its duration (#2266 ite…
erikdarlingdata Aug 15, 2026
cc5839f
Choose the shipped rows before touching the query text (#2150) (#2288)
erikdarlingdata Aug 15, 2026
fdb316d
Revert "Choose the shipped rows before touching the query text (#2150…
erikdarlingdata Aug 16, 2026
ec45bcf
Credit a newly compiled plan's counter instead of discarding it (#223…
erikdarlingdata Aug 16, 2026
4f2fed3
Add a watermarked query-text fetch seam, default off (#2150) (#2291)
erikdarlingdata Aug 16, 2026
a35e2aa
Store query_store statement text out of the sorted stream (V74, #2150…
erikdarlingdata Aug 16, 2026
d1e1e58
Stop MCP losing the server registry over a password it never reads (#…
erikdarlingdata Aug 16, 2026
2891e1f
Say "timed out" when a baseline query times out (#2150 dogfood find) …
erikdarlingdata Aug 16, 2026
4d11ad8
Resolve Query Store text from collect.query_store_text and turn the s…
erikdarlingdata Aug 16, 2026
40319e7
Source the MCP host's server map from the worker's privileged load (#…
erikdarlingdata Aug 17, 2026
10d79fa
Cap the Index Analysis banners so they cannot evict the Recommendatio…
erikdarlingdata Aug 17, 2026
696eb76
Give the analysis pass a stopping token and classify shutdown residue…
erikdarlingdata Aug 17, 2026
4af363c
Serve a sweep_pressure verdict from get_collection_health so half-rat…
erikdarlingdata Aug 17, 2026
77aaebe
Stop the review guard converting its own lookup failures into verdict…
erikdarlingdata Aug 17, 2026
cbdf957
Guard the guard's own plumbing: mktemp/cat failures degrade instead o…
erikdarlingdata Aug 17, 2026
2a50ce9
Give the generic webhook raw-JSON context tokens and the cross-channe…
erikdarlingdata Aug 17, 2026
1cc8604
Clear FinOps column filters on server switch (#2306) (#2315)
erikdarlingdata Aug 17, 2026
1e92720
Ship closed Query Store intervals every cycle, the open interval on a…
erikdarlingdata Aug 17, 2026
643c8ac
Label the lifetime extremes on the top-CPU reads and flag the provabl…
erikdarlingdata Aug 18, 2026
24ed7b5
Split the plan-XML and text fetches out of the query_store drain timi…
erikdarlingdata Aug 18, 2026
ffec6fc
Wire the plan fetch's adaptive candidate sizing (#2312 Finding 1) (#2…
erikdarlingdata Aug 18, 2026
7376861
Gap-marked series render line-only: the gradient fill goes opaque bla…
erikdarlingdata Aug 18, 2026
67825cf
V75: plan content gets its own retention horizon (#2316) (#2326)
erikdarlingdata Aug 18, 2026
1e48f95
Give the self-metrics sweep a real timeout, and name a timeout as one…
erikdarlingdata Aug 18, 2026
f990da8
query_store_health: per-database Query Store health as an hourly time…
erikdarlingdata Aug 18, 2026
525c486
get_query_store_health: the MCP read for the new collector, both SKUs…
erikdarlingdata Aug 18, 2026
2ceaaa9
cpu_attribution on the top-CPU rankings: what fraction of the box the…
erikdarlingdata Aug 18, 2026
9f01385
Darling Viewer: Query Store grid DarkButton crash — Lite's #2114 on t…
erikdarlingdata Aug 18, 2026
6ce8413
3.5.0 release prep: version bump, changelog cut, review prompt fix (#…
erikdarlingdata Aug 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
11 changes: 9 additions & 2 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,18 @@ updates:
interval: "weekly"
day: "monday"
open-pull-requests-limit: 10
# One grouped PR for all NuGet bumps, to keep review noise down.
# One grouped PR for routine NuGet bumps, to keep review noise down. Majors are
# deliberately NOT in the group (Studio's convention): a breaking change arrives as
# its own PR instead of buried in a routine batch. With central package management
# (Directory.Packages.props) each bump is one line, so grouped PRs can no longer
# ship the #2100 class of multi-project version misalignment.
groups:
nuget:
nuget-patch-and-minor:
patterns:
- "*"
update-types:
- "minor"
- "patch"
# GitHub Actions used by the workflows in .github/workflows.
- package-ecosystem: "github-actions"
directory: "/"
Expand Down
1,748 changes: 876 additions & 872 deletions .github/workflows/build.yml

Large diffs are not rendered by default.

285 changes: 285 additions & 0 deletions .github/workflows/claude-review-guard.yml

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions .github/workflows/claude-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ jobs:
REPO: ${{ github.repository }}
PR NUMBER: ${{ github.event.pull_request.number }}

Review this pull request. Follow the conventions in CLAUDE.md and the T-SQL style
guide it points to. This repository ships two apps that must stay in parity — Lite
Review this pull request. Follow the conventions in CONTRIBUTING.md — including
its T-SQL style section (AS on table aliases, column_name = expression aliasing,
OPTION(RECOMPILE) on collector queries). This repository ships two apps that must stay in parity — Lite
and Darling — so flag any change made to one but not its counterpart. Focus on:
- Correctness: bugs, edge cases, null/error handling at system boundaries
- Lite/Darling parity drift
Expand Down
974 changes: 487 additions & 487 deletions .github/workflows/nightly.yml

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,9 @@ TestResults/
# holds a LIVE database password in cleartext.
viewer-config/
darling.json

# Local verification harnesses for the PostgreSQL work (Darling/tools/pg-harnesses). Throwaway net10.0
# console apps that reference the real projects, standing in for test suites that cannot execute on macOS.
# Deliberately untracked for now: whether pincheck in particular belongs in the repo is Erik's call, and it
# should not join a PR by accident.
Darling/tools/pg-harnesses/
223 changes: 223 additions & 0 deletions CHANGELOG.md

Large diffs are not rendered by default.

30 changes: 29 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,35 @@ ORDER BY
OPTION(RECOMPILE);
```

The full T-SQL style guide is in [CLAUDE.md](CLAUDE.md).
A few more rules that come up in review, and the reasoning behind the ones that are
not obvious:

- **Unicode literals**: prefix with `N` (`N'ONLINE'`, not `'ONLINE'`).
- **`ON` continues its `JOIN` at two spaces**, so the join graph reads down the left edge.
- **`AND` / `OR` align their predicates** (`AND d.state_desc = N'ONLINE'`), so a `WHERE`
clause reads as a list rather than as prose.
- **`GROUP BY` / `ORDER BY` put each term on its own indented line**, so adding one is a
one-line diff.
- **Never suggest missing-index DMV recommendations.** `sys.dm_db_missing_index_*` output
is not used in this project and changes proposing it will not be accepted.
- **No full-text search.**

Collector queries specifically:

- **`OPTION(RECOMPILE)` on collector queries.** These run with parameters whose selectivity
varies enormously between a first-run catch-up window and a steady-state minute, and a plan
cached from one is wrong for the other. A statement added to an existing batch needs its own
hint — one on a neighbouring statement does not cover it.
- **Comments explain WHY, at length.** This codebase's comments carry measurements, issue
numbers, and the failure the line prevents. A comment restating the code is noise; one
recording "this threshold was 300s and the fleet's median gap is 299s, so it discarded half
of every sweep" is what stops the next person undoing it.

Darling's PostgreSQL store (not T-SQL — the Darling service stores to PostgreSQL/TimescaleDB):

- **Schema-qualify every object in a migration** (`collect.*`, `config.*`). The migrate session's
`search_path` resolves bare names to a different schema, so an unqualified `CREATE` or `ALTER`
can land an object in the wrong one silently.

### C# Style

Expand Down
189 changes: 189 additions & 0 deletions Darling/Darling.Tests/AddServerVerbTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
/*
* Copyright (c) 2026 Erik Darling, Darling Data LLC
*
* This file is part of the SQL Server Performance Monitor.
*
* Licensed under the MIT License. See LICENSE file in the project root for full license information.
*/

using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using PerformanceMonitor.Darling.Service;
using Xunit;

namespace Darling.Tests;

/// <summary>
/// #2256: the <c>--add-server</c> verb, which exists because a headless host had no supported way to register a
/// monitored server at all.
///
/// <para><b>Why it was impossible.</b> <c>darling.json</c> seeds the registry only while it is empty, so file
/// edits after the first start are ignored (#2254); the web surface keeps <c>add_servers</c> off
/// <c>/api/read/*</c> deliberately because it writes; and there was no CLI verb. The field report ran the
/// service on Windows Server 2012, which cannot run the Viewer, so the only remaining routes were a GUI on
/// another machine or standing up an MCP client.</para>
///
/// <para><b>What is pinned here</b> is the part that has no store in it: verb recognition, the stdin contract,
/// and the result formatting plus its exit-code policy. The registration itself is
/// <c>DarlingMcpServerAdminTools.AddServers</c>, already covered by its own tests — the verb deliberately adds
/// no second implementation of validation, dedupe, probing, encryption or identity computation.</para>
/// </summary>
public sealed class AddServerVerbTests
{
[Theory]
[InlineData("--add-server")]
[InlineData("--add-servers")]
[InlineData("--ADD-SERVER")]
public void BothSpellingsAreRecognized_CaseInsensitively(string arg)
{
Assert.True(DarlingCliCommands.IsAddServerVerb(arg));

/* And the classifier must dispatch it rather than fall through to starting the host — the #1581
incident was a verb that reached a real startup and spawned a second instance. */
Assert.Equal(StartupAction.RunKnownVerb, DarlingCliCommands.ClassifyStartupArgs(new[] { arg }));
}

[Fact]
public void TheVerbIsDiscoverable_FromHelp()
{
Assert.Contains("--add-server", DarlingCliCommands.UsageText(), StringComparison.Ordinal);
}

/// <summary>
/// Empty stdin must EXPLAIN itself on stdout and change nothing.
///
/// <para>Stdout rather than stderr is the [#2097] lesson: in the PowerShell ISE, remoting sessions and some
/// integrated terminals stderr is not surfaced, so a verb that writes only there reads as hung — which is
/// exactly how the first setup step was reported as broken.</para>
/// </summary>
[Fact]
public async Task EmptyStdin_ExplainsItselfOnStdout_AndChangesNothing()
{
var output = new StringWriter();
var error = new StringWriter();

/* No config path and no store: it must return before touching either, which is itself the assertion —
a store connection here would throw rather than return 1. */
var exit = await DarlingCliCommands.AddServerAsync(
configPath: null, input: new StringReader(string.Empty), output: output, error: error,
cancellationToken: CancellationToken.None);

Assert.Equal(1, exit);
Assert.Equal(string.Empty, error.ToString());

var text = output.ToString();
Assert.Contains("stdin", text, StringComparison.OrdinalIgnoreCase);
Assert.Contains("--add-server", text, StringComparison.Ordinal);
/* The reason the password is not an argument, stated where the operator will look for it. */
Assert.Contains("process list", text, StringComparison.OrdinalIgnoreCase);
}

[Fact]
public async Task WhitespaceOnlyStdin_IsTreatedAsEmpty()
{
var output = new StringWriter();
var exit = await DarlingCliCommands.AddServerAsync(
null, new StringReader(" \r\n "), output, new StringWriter(), CancellationToken.None);

Assert.Equal(1, exit);
Assert.Contains("stdin", output.ToString(), StringComparison.OrdinalIgnoreCase);
}

/// <summary>A server that landed: the ADDED line, and the sentence that saves the operator a restart they do
/// not need — the registry write bumps <c>config_version</c>, which the worker polls every sweep.</summary>
[Fact]
public void AnAddedServer_ReportsItAndSaysNoRestartIsNeeded()
{
var (lines, exit) = DarlingCliCommands.FormatAddServerOutcome(
"""{"added":1,"skipped":0,"failed":0,"results":[{"server":"sql01","status":"added","detail":"SQL major version 16"}]}""");

Assert.Equal(0, exit);
Assert.Contains(lines, l => l.Contains("[ADDED] sql01", StringComparison.Ordinal));
Assert.Contains(lines, l => l.Contains("SQL major version 16", StringComparison.Ordinal));
Assert.Contains(lines, l => l.Contains("1 added, 0 already registered, 0 failed.", StringComparison.Ordinal));
Assert.Contains(lines, l => l.Contains("no restart is needed", StringComparison.OrdinalIgnoreCase));
}

/// <summary>Re-running the same file is idempotent, not a failure — so a batch of pure duplicates exits 0
/// and does NOT claim a restart is pending, because nothing changed.</summary>
[Fact]
public void PureDuplicates_ExitZero_AndPromiseNoReload()
{
var (lines, exit) = DarlingCliCommands.FormatAddServerOutcome(
"""{"added":0,"skipped":2,"failed":0,"results":[{"server":"a","status":"duplicate","detail":"already registered"},{"server":"b","status":"duplicate","detail":"already registered"}]}""");

Assert.Equal(0, exit);
Assert.Equal(2, lines.Count(l => l.Contains("[SKIP]", StringComparison.Ordinal)));
Assert.DoesNotContain(lines, l => l.Contains("no restart", StringComparison.OrdinalIgnoreCase));
}

/// <summary>Anything that failed is a non-zero exit, even alongside a success — the verb is usable as a
/// deployment gate, and a partial batch must not read as clean.</summary>
[Fact]
public void AnyFailure_ExitsNonZero_EvenBesideASuccess()
{
var (lines, exit) = DarlingCliCommands.FormatAddServerOutcome(
"""{"added":1,"skipped":0,"failed":1,"results":[{"server":"good","status":"added","detail":"ok"},{"server":"bad","status":"connection_failed","detail":"login failed"}]}""");

Assert.Equal(1, exit);
Assert.Contains(lines, l => l.Contains("[FAIL] bad", StringComparison.Ordinal));
Assert.Contains(lines, l => l.Contains("login failed", StringComparison.Ordinal));
}

/// <summary>Nothing landed at all — an empty array, or every entry rejected — must not report success to a
/// script. A verb that changed nothing and exits 0 is the failure mode this policy exists for.</summary>
[Theory]
[InlineData("""{"added":0,"skipped":0,"failed":0,"results":[]}""")]
[InlineData("""{"added":0,"skipped":0,"failed":1,"results":[{"server":"x","status":"invalid","detail":"unsupported auth"}]}""")]
public void NothingLanded_ExitsNonZero(string json)
{
var (_, exit) = DarlingCliCommands.FormatAddServerOutcome(json);

Assert.Equal(1, exit);
}

/// <summary>The whole-payload rejection shape carries no <c>results</c> array (the tool returns it without
/// opening the store when the JSON is not an array at all), so the formatter must render its message rather
/// than throw on the missing property.</summary>
[Fact]
public void AWholePayloadRejection_IsRenderedNotThrown()
{
var (lines, exit) = DarlingCliCommands.FormatAddServerOutcome(
"""{"status":"invalid","message":"servers_json must be a JSON array"}""");

Assert.Equal(1, exit);
Assert.Contains(lines, l => l.Contains("must be a JSON array", StringComparison.Ordinal));
}

/// <summary>
/// A store failure AFTER the request parsed does not arrive as JSON at all: <c>AddServersAsync</c>'s
/// catch-all returns <c>McpHelpers.FormatError</c>, which is plain text. That text IS the message the
/// operator needs, so it must be surfaced verbatim rather than buried under a "could not parse" wrapper —
/// which is what happened before, precisely when the verb is being used as a deployment gate.
/// </summary>
[Fact]
public void APlainTextStoreError_IsSurfacedVerbatim_NotWrapped()
{
var (lines, exit) = DarlingCliCommands.FormatAddServerOutcome(
"Error during add_servers: 57P01: terminating connection due to administrator command");

Assert.Equal(1, exit);
Assert.Contains(lines, l => l.Contains("57P01", StringComparison.Ordinal));
Assert.DoesNotContain(lines, l => l.Contains("Could not parse", StringComparison.Ordinal));
}

/// <summary>Something that LOOKED like JSON and was not still says so, and still shows the payload — the
/// two cases are told apart by shape so neither hides the other.</summary>
[Fact]
public void MalformedJson_SaysSo_AndShowsThePayload()
{
var (lines, exit) = DarlingCliCommands.FormatAddServerOutcome("{\"added\":1, oops");

Assert.Equal(1, exit);
Assert.Contains(lines, l => l.Contains("Could not parse", StringComparison.Ordinal));
Assert.Contains(lines, l => l.Contains("oops", StringComparison.Ordinal));
}
}
Loading
Loading