Files
meshcore-analyzer/test-issue-1456-score-labels.js
T
Kpa-clawbot d24246395d fix(#1456): rename Usefulness → Traffic share + add traffic_share_score field (#1457)
## Summary

Rename the "Usefulness" UI label to "Traffic share", add hover tooltips
for both Traffic share and Bridge score, and introduce a new
`traffic_share_score` field on `/api/nodes` (alongside the legacy
`usefulness_score`, kept for API back-compat).

Closes #1456.

## Why

The "Usefulness" label implied a composite score that doesn't exist yet
— only the Traffic-share axis (axis 1 of 4 from #672) and the Bridge
axis (axis 2 of 4 from #1275) are wired today. A node with low traffic
but critical structural position read as "not useful" — exactly wrong.
Neither score had a tooltip explaining what it measured.

## Changes

### Frontend (`public/nodes.js`)
- Visible label `Usefulness` → `Traffic share` (with ⓘ glyph)
- Tooltip explains traffic-share semantics, cross-references Bridge for
structural importance, points at #672 for the 4-axis roadmap
- Bridge row gets a parallel ⓘ glyph and a tooltip naming "betweenness
centrality" + the "quiet but irreplaceable chokepoint" interpretation
- Prefers new `traffic_share_score` with graceful fallback to legacy
`usefulness_score`

### Backend (`cmd/server/routes.go`)
- `/api/nodes` and `/api/nodes/{pubkey}` now emit BOTH
`usefulness_score` (kept for API compat) AND `traffic_share_score` (new
canonical name), populated with the same value
- Inline comment documents the deprecation path: when the #672 composite
ships, `usefulness_score` becomes the composite and
`traffic_share_score` keeps the per-axis value

## Tests

- `test-issue-1456-score-labels.js` — file-grep pins on `nodes.js`
(label, tooltip fragments, percent formatting, dual-field read with
fallback)
- `cmd/server/traffic_share_score_test.go` — `/api/nodes` +
`/api/nodes/{pk}` responses contain both fields with equal values

TDD: red commit (`8bd235a0`) added failing tests; green commit
(`c4d3aee5`) implemented. `go test ./cmd/server/...` passes (47s).

## Out of scope

- Renaming the backend field (would break consumers)
- Wiring axes 3 (Coverage) and 4 (Redundancy) — tracked in #672
- Changing the score calculation

---------

Co-authored-by: clawbot <bot@openclaw.local>
2026-05-28 05:22:08 -07:00

72 lines
3.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Issue #1456 — UI rename "Usefulness" → "Traffic share" + tooltips
* for both Traffic share and Bridge score.
*
* Backend keeps `usefulness_score` for API compat; a new
* `traffic_share_score` field carries the same value and is what new
* UI consumers should read (UI falls back to usefulness_score for
* graceful degradation). The Go-side surface check for the new field
* lives in cmd/server/traffic_share_score_test.go.
*
* This file pins the frontend pieces by file-grep on nodes.js.
*/
'use strict';
const fs = require('fs');
const path = require('path');
let passed = 0, failed = 0;
function assert(cond, msg) {
if (cond) { passed++; console.log(' ✓ ' + msg); }
else { failed++; console.error(' ✗ ' + msg); }
}
const src = fs.readFileSync(path.join(__dirname, 'public', 'nodes.js'), 'utf8');
console.log('\n=== #1456: nodes.js renders "Traffic share" label ===');
// The node-stats-table row that surfaces the score. Locate the row by
// its stable id `row-usefulness-score` (kept for backwards-compat /
// DOM hooks).
const rowIdx = src.indexOf('id="row-usefulness-score"');
assert(rowIdx > 0, 'usefulness-score row block present in nodes.js');
const rowBlock = src.slice(Math.max(0, rowIdx - 400), rowIdx + 800);
assert(rowBlock.includes('Traffic share'),
'row block contains visible label "Traffic share"');
assert(!/>\s*Usefulness\s*</.test(rowBlock),
'row block no longer renders ">Usefulness<" as visible cell text');
console.log('\n=== #1456: tooltip text present for both scores ===');
assert(/betweenness centrality/i.test(src),
'nodes.js contains tooltip fragment "betweenness centrality" (Bridge tooltip)');
assert(/non-advert (mesh )?traffic/i.test(src),
'nodes.js contains tooltip fragment "non-advert traffic" (Traffic share tooltip)');
assert(/chokepoint|irreplaceable/i.test(src),
'nodes.js Bridge tooltip mentions chokepoint/irreplaceable framing');
console.log('\n=== #1456: percent formatting near score render ===');
const usefulRender = src.slice(rowIdx - 600, rowIdx + 600);
assert(/\*\s*100\)?\s*\.toFixed\(1\)/.test(usefulRender) ||
/toFixed\(1\)\s*\+\s*['"]%['"]/.test(usefulRender) ||
/\$\{pct\}%/.test(usefulRender),
'traffic-share render emits percent-formatted value (×100 + toFixed(1))');
console.log('\n=== #1456: UI prefers traffic_share_score, falls back to usefulness_score ===');
// New frontend reads the new field with graceful fallback so stale
// servers (still only emitting usefulness_score) keep working.
assert(/traffic_share_score/.test(src),
'nodes.js references the new traffic_share_score field');
const fallbackRe = /n\.traffic_share_score\s*[!=]=\s*null\s*\?\s*n\.traffic_share_score\s*:\s*n\.usefulness_score/;
assert(fallbackRe.test(src) ||
/traffic_share_score\s*\?\?\s*n?\.?usefulness_score/.test(src) ||
/traffic_share_score[\s\S]{0,80}usefulness_score/.test(src),
'nodes.js falls back to usefulness_score when traffic_share_score is absent');
console.log('\n────────────────────────────────────────');
console.log(` ${passed} passed, ${failed} failed`);
process.exit(failed ? 1 : 0);