Raised in review of #1837's PR (#1854) and deliberately not fixed there.
All three Collection Health reads project the note as
MAX(CASE WHEN status = 'SUCCESS' THEN error_message END) AS last_note
(Lite/Services/LocalDataService.CollectionHealth.cs, Darling/PerformanceMonitor.Darling.Viewer/ViewerDataService.CollectionHealth.cs, Darling/PerformanceMonitor.Darling.Service/Mcp/DarlingDataReader.cs).
MAX over text is the lexicographic greatest string in the window, not the most recent one. That is the shape the sibling last_error column has always had, and it was harmless there because a failure mode's text is stable. #1837 introduced the first note whose text carries a NUMBER:
{N} item(s) failed their enumeration probe - see the app log for the per-item errors
Lexicographic order does not sort numbers, so "12 item(s)..." sorts BELOW "9 item(s)...". Across a window where the probe-failure count varies cycle to cycle, the grid can therefore display an arbitrary historical run's count rather than the newest. Secondary: DuckDB (binary) and Postgres (locale) collations can pick different strings from identical data, so Lite's grid and the Darling Viewer's can disagree on the same row.
Bounded, but real. The empty-enumeration note carries no number and is unaffected, which is the primary case the feature exists for. The probe note explicitly points at the app log for per-item detail, and the operator's next action is identical whichever count is shown. The per-cycle count is exact on the Collection Log detail grid, which shows the raw rows. So the wrong number, when it happens, is a display imprecision on a summary column, not a wrong verdict.
Why it was not fixed in the PR. The obvious fix is "the message from the row with the greatest collection_time", and there is no portable aggregate for that: DuckDB has arg_max, PostgreSQL has no max_by. These three SQL strings are deliberately byte-portable between the two stores (positional parameters, plain aggregates only) and a dialect split there is a bigger cost than the defect. The workarounds considered and rejected: concatenating a sortable timestamp prefix onto the message and stripping it in the mapper (works, but makes three shared queries non-obvious for a low-impact wart), and removing the count from the note text (loses the per-cycle count on the detail grid, where it is exact and useful).
Options when picked up: accept a per-store expression for this one column; add a last_note_time and select the co-located message via a window function; or restructure so the count is a column rather than part of the text. Whichever route, note_count / total_runs and the FormatCollectionNote qualifier are already order-independent and correct - only the exemplar text is affected.
Raised in review of #1837's PR (#1854) and deliberately not fixed there.
All three Collection Health reads project the note as
(
Lite/Services/LocalDataService.CollectionHealth.cs,Darling/PerformanceMonitor.Darling.Viewer/ViewerDataService.CollectionHealth.cs,Darling/PerformanceMonitor.Darling.Service/Mcp/DarlingDataReader.cs).MAXover text is the lexicographic greatest string in the window, not the most recent one. That is the shape the siblinglast_errorcolumn has always had, and it was harmless there because a failure mode's text is stable. #1837 introduced the first note whose text carries a NUMBER:Lexicographic order does not sort numbers, so
"12 item(s)..."sorts BELOW"9 item(s)...". Across a window where the probe-failure count varies cycle to cycle, the grid can therefore display an arbitrary historical run's count rather than the newest. Secondary: DuckDB (binary) and Postgres (locale) collations can pick different strings from identical data, so Lite's grid and the Darling Viewer's can disagree on the same row.Bounded, but real. The empty-enumeration note carries no number and is unaffected, which is the primary case the feature exists for. The probe note explicitly points at the app log for per-item detail, and the operator's next action is identical whichever count is shown. The per-cycle count is exact on the Collection Log detail grid, which shows the raw rows. So the wrong number, when it happens, is a display imprecision on a summary column, not a wrong verdict.
Why it was not fixed in the PR. The obvious fix is "the message from the row with the greatest
collection_time", and there is no portable aggregate for that: DuckDB hasarg_max, PostgreSQL has nomax_by. These three SQL strings are deliberately byte-portable between the two stores (positional parameters, plain aggregates only) and a dialect split there is a bigger cost than the defect. The workarounds considered and rejected: concatenating a sortable timestamp prefix onto the message and stripping it in the mapper (works, but makes three shared queries non-obvious for a low-impact wart), and removing the count from the note text (loses the per-cycle count on the detail grid, where it is exact and useful).Options when picked up: accept a per-store expression for this one column; add a
last_note_timeand select the co-located message via a window function; or restructure so the count is a column rather than part of the text. Whichever route,note_count/total_runsand theFormatCollectionNotequalifier are already order-independent and correct - only the exemplar text is affected.