Repository navigation
The fleet card reads a server dark for over 48 hours as Offline, not 'Awaiting first collection' (#3935) - #3966
Merged
Conversation
…"Awaiting first collection" (#3935) FleetLastCollectionSql keeps its 48-hour window (Erik's ruling, option 2). A server dark for longer fell out of it, reached ClassifyFreshness as a null and read "Awaiting first collection" (Warning), while the WPF viewer's unbounded read called it Offline. The read is now a LEFT JOIN LATERAL, so every enabled registry server is reported, with a null newest collection when the window holds none. It carries the registry's created_date, the first successful connect. ClassifyWindowedFreshness is the shared ladder plus one arm: - an empty window on a server registered before the window is Offline; - one registered inside it is still awaiting its first collection, which is exact because the worker only collects after connecting. A server missing from the read's result altogether gets the row's default (two nulls) and keeps the no-claim reading. That is the lesson of the 2026-08-30 false-Offline fix. Same statement count, same plan, same buffers on DARLING01. With no window, cold planning is 89-101 ms against 25-29 ms with it. The web card's Offline status line now shows an age ("last collect 20h ago") or "no recent collection" instead of a bare time of day or an em dash. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018Z4T51J4E6L8fjriPHpsif
erikdarlingdata
enabled auto-merge (squash)
September 23, 2026 01:30
erikdarlingdata
added a commit
that referenced
this pull request
Sep 23, 2026
…3920, #3927, #3931, #3932, #3940, #3942, #3946, #3947, #3950, #3952, #3955, #3956, #3957, #3964, #3965, #3966, #3968, #3972, #3975, #3979, #3980, #3981, #3983, #3984, #3985) (#3989) The wave's fix PRs deliberately carried no CHANGELOG edits (parallel-agent hot-spot protocol); each agent reported its entry and this commit lands them together, byte-verified against origin/dev. Claude-Session: https://claude.ai/code/session_01GdmA4ND1wLSqA91ax1m4xv Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #3935.
Why
The fleet card reads each server's newest collection from
DarlingFleetReader.FleetLastCollectionSql. That read looks back 48 hours. A server dark for longer fell out of it and reachedClassifyFreshnessas a null, and a null there means never collected. So the card said "Awaiting first collection" (Warning), and the Needs-attention reason said the same. The WPF viewer reads the same timestamp with no window and calls the server Offline. Only the viewer was right: the server collected once, then stopped.The diagnosis in the issue holds.
FleetReadsAreBoundedLivePostgresTestsseeds a server dark for five days, and its card readIsOnline = nulland awaiting first collection.Erik's ruling (option 2): keep the 48-hour window. Tell "never collected" apart from "nothing in the window" using the registry row or the collection history, cheaply, and match the viewer's banding.
What changes
The last-collection read reports every enabled server, with its registration.
FleetLastCollectionSqlis still #3947's per-serverLIMIT 1probe, still bound to 48 hours on the partition column. Two things change:LEFT JOIN LATERAL ... ON TRUE, so a server with nothing in the window is reported with a null instead of being dropped.s.created_datefrom the registry. That column is the server's first successful connect: the upsert writes it on insert and never again.There is no extra round trip and no second look at
collection_log.ClassifyWindowedFreshnessdecides what an empty window means. It is the shared ladder plus one arm:created_date.Only a positive report can turn into Offline. A server that is missing from the read's result altogether gets the row's
default: two nulls, which classify as never collected. That can only happen if the server is disabled between the two reads. The null fallback was written after actively-collecting servers showed as Offline on a production store (a missing row became a year-0001 timestamp), and a missing row still can't produce Offline.The window stays, for the price the issue measured. The bind and the classifier share one derivation,
LastCollectionWindowStart(now), so the window the read searched and the window the card reasons about cannot drift apart.Web card. The status line on an Offline card showed a bare time of day (
last collect 01:09 AMfor a collection 20 hours old). It now shows an age (last collect 20h ago), orno recent collectionwhen the window holds nothing. It used to renderlast collect —in that case. I checked this by rendering the shippedfleet.jsunder a DOM shim, against HEAD and this branch.MCP payload. No shape change and no new fields.
status/band/is_online/awaiting_first_collection/reasonnow read Offline for such a server.last_collectionstays the newest collection inside the window, so it is null for a server dark longer than two days.statustells that null apart from the never-collected one, and the field's doc says so. The tool description needed no change: every field is still read for the call.Behavior change, stated. A server dark for more than 48 hours now counts in
offline_countrather thanwarning_count, and ranks as Offline inworst_servers.Where the card and the viewer can still differ, deliberately. The viewer says "Awaiting first collection" whenever it finds no
collection_logrow at all. That covers two cases:collection_logretention. The viewer andlist_serversside of that is filed as A server dark past collection_log's 60-day retention reads 'Awaiting first collection' in the WPF viewer and list_servers #3967.The card reads both as Offline. In both, the service reached the server and nothing has come back for two days.
Lite: no change needed. Lite's last-collection read has no window (hot table, then the archive view), and Lite has no fleet roll-up.
Not changed: the viewer,
list_servers,FleetServersSql, the 7-day collection-health scan, and no migration.Test plan
DARLING01 (read-only,
-U admin,default_transaction_read_only), 19collection_logchunks, 16 compressed. Before and after were run alternately in fresh sessions, two rounds each. The plans are the same (Nested Loop becomes Nested Loop Left Join), with the same buffers:FleetLastCollectionSqlcreated_date)Cold-planning figures vary across sessions (an earlier round measured 9.8–13.1 ms for the windowed read). Each comparison above comes from back-to-back sessions.
New
FleetCardTellsDarkFromNeverCollectedTests(7 tests):is_online, reason and roll-up counts;collection_stalekeeps its population (has_collector_errorsdrives the fleet card's health band and its semantics cannot be determined from the surface — four wrong inferences by two independent agents, and it readsfalsewhile errors sit inside the card's own window #3098);created_date, the window kept, onecollection_logreference, the shared bind.FleetReadsAreBoundedLivePostgresTests(rig, TimescaleDB):GetFleetOverviewAsync, the five-days-dark card reads Offline and the ten-minutes-registered one reads awaiting, each matching the word the WPF viewer's unbounded freshness gives;Updated pins, deliberately:
FleetLastCollectionSql_IsWindowed_NotAFullHistoryScan(CROSS to LEFT) andTheMcpCard_DerivesTheFlagFromFreshnessAndNothingElse(the classifier call).Red-watch, test project rebuilt each time. Four mutations of the reader, each caught:
Full
Darling.TestswithDARLING_TEST_PGon the rig: 12,603 total, 0 failed, 22 skipped. Every skip is gated on an environment this run did not set: the managed runtime or upgrade fixtures, a live SQL Server, symlink privilege, or the CI worker sizing. The first full run went red on two meta pins this change tripped, and both are fixed in the change.RepoFileAdoptionTestsfailed because the new test read the reader LF-normalised for single-line anchors, and it now uses the raw reader.TsqlConventionGuardTests.TheMemberScan_ReadsEveryDeclarationWholefailed because the member walker stops at a property pattern's braces, so the classifier is now written without one. Neither pin file is edited.Build:
0 Warning(s).🤖 Generated with Claude Code
https://claude.ai/code/session_018Z4T51J4E6L8fjriPHpsif