Repository navigation
Tie the AG page's rollup numbers to their labels the way the fleet page's now are #3045
Description
Activity
- added a commit that references this issue
on Sep 6, 2026 Claude posting for Erik Darling
Closed by #3054, merged to
devatf1bf0386079e8170e5dda3181f8f91eb65000b0c. Closing by hand because a closing keyword does not fire againstdev.Every rollup figure on both web pages is now programmatically tied to the text that says what it counts.
The decision this issue carried was the hoist, and it went the way the issue argued.
rollupTextIdmoved fromfleet.jsintoutil.js— identical exceptfunction→export function, which is the keyword the hoist requires; worth stating precisely so a later reviewer who diffs it and finds a difference does not think something slipped. Both pages now share one de-duplication rule.The grounds, because they are the interesting part: the
usedset is inert on the AG page today, since its three labels differ and cannot collide. That is exactly why a local copy would have been written without it and stayed correct by luck until a fourth tile repeated a label — at which pointaria-describedbyresolves a duplicate id to the FIRST match, a silent mis-association rather than a visible break.subIddeliberately did not come across, and its absence is pinned rather than left to the next reader's restraint: the AG tiles qualify nothing and hover over nothing, so there is no second text row for an id to point at, and hoisting an unused parameter invites a caller to pass something meaningless.The guard fails on the defect rather than passing on the fix, which is what this issue asked for.
ag.jsrestored to whatdevshipped reds four tests;.numrenamed on both pages reds the population floor withfound 0. Nine mutations, each landing on a different named assertion, run against a sandbox copy after commit.The subtle part is worth recording: "every tile is wired" is not a per-tile source fact, because three tiles come from one closure.
EveryRollupPage_BuildsEveryTileFromOneClosureis what converts it into one —tiles == 1 && figures == 1per page means a hand-rolled fourth tile is a second construction and reds even if it carries a description.Not verified: nothing was rendered and no screen reader was run. Behaviour was exercised under a DOM shim in node against the shipped modules, including the duplicate-label control — guard in place, the third tile gets
rollup-groups-lbl-2; guard removed, the figure reading 9 is described by the label of the tile reading 4.The same gap exists on the desktop surface and is filed separately rather than left in a chip. It is larger than this issue:
AutomationPropertiesappears nowhere in the Viewer,PerformanceMonitor.Uior Lite.
#3038 fixed the fleet page's rollup tiles: every tile's number is now programmatically tied to the text that says what it counts, so a consumer reaching the number's node on its own gets the figure and its meaning together. The AG page's rollup has the identical defect and was deliberately left out of that PR, because the two pages' tile builders share no code path and one PR touching both would have been reviewed as two changes wearing one number.
The site
Darling/PerformanceMonitor.Darling.Service/wwwroot/js/pages/ag.js,rollup()at line 97:Three tiles ride on it —
Groups,Reporting servers,Views. The number and its label are sibling divs and nothing else. Reading order leaves the relationship inferable but not determinable, which is the same thing #3031 was about.Correcting how this was scoped when #3031 closed
I described the fix as "the
util.jshoist is the shape." There is noutil.jshoist.rollupTextIdand the wiredtileclosure both live infleet.js(lines 480–525) and nothing inutil.jsexposes a tile or id helper. So this is not "call the existing shared helper" — the shared helper does not exist yet, and creating one is the actual decision this issue carries.Two ways to go, and the choice is worth making deliberately rather than by whichever is quicker:
rollupTextIdintoutil.jsand have both pages use it. It is eight lines with no page-specific knowledge — a label, a part name, and ausedset — so it hoists cleanly. This keeps one de-duplication rule for both pages, which matters because the rule is subtle (see below).ag.js. Cheaper to review in isolation, and leaves the two pages free to diverge. But it makes the de-duplication rule a thing that exists twice, and a rule that exists twice is a rule that will be fixed once.I lean to the hoist, for the reason
fleet.jsalready documents: theusedset exists because two tiles sharing a label emit a duplicate id, andaria-describedbyresolves a duplicate to the FIRST match — a silent mis-association rather than a visible break. That is the kind of hazard you want one implementation of. AG's three current labels are distinct, so the set is inert there today, which is exactly why a local copy would be written without it and be correct until someone adds a fourth tile.What not to copy from fleet.js
The sub-line machinery.
fleet.js's tile takessubandtitlebecause the fleet rollup carries a coverage sub-line and a long coverage note; the AG rollup has neither, and six of the seven fleet tiles have no sub-line either.subIdshould not appear in an AG implementation just because it appears in the model. The part that transfers is the number describing itself with the label's id.Also worth keeping: the reason the label rides in
aria-describedbyrather thanaria-labeloraria-labelledby—.numis a plain div, ARIA prohibits namingrole=generic, so a name set there is invalid and may be dropped, while a description is a supported property. That reasoning is page-independent and should not be re-derived.Verification this needs
Whatever shape it takes, the guard should fail on a tile whose number carries no description — not merely pass on the tiles that have one. A positive-only check cannot find an unremembered site, which is the whole reason this page survived #3031.