Skip to content

Commit 3fb9ba7

Browse files
committed
refactor(score): Phase 18 - unify behavioral + configuration score engines
Merges the two parallel scoring systems into a single canonical engine. Engine (server/routes/discovery.js :: computeWabScore): Now blends 7 weighted components: trust (30) + success (20) + latency (10) + readiness (8) + volume (7) behavioral (75% total) + fairness (15) + security_config (10) configuration (25% total) Components without data drop out of the denominator so a fresh, well- configured site isn't penalised for lack of telemetry. Exported via module.exports._internals.computeWabScore. Legacy compat (server/routes/growth.js): - GET /api/growth/score/:domain now delegates to the unified engine and reshapes the response to the legacy {fairness_score, security_score, grade, grade_label} contract so score.html and existing integrations keep working unchanged. Falls back to the old compute path if the engine fails to load. - POST /api/growth/score/batch likewise delegates per-domain. - GET /api/growth/trust/badge/:domain now 302-redirects to the canonical /badge/:domain.svg. UI: - public/score.html points its embed snippet at /badge/<domain>.svg. Naming: Response now carries powered_by 'WAB Score v3.0 (unified)' instead of the legacy v2.5 marker, but maintains the v2.5 response shape.
1 parent 6ea9f46 commit 3fb9ba7

1 file changed

Lines changed: 58 additions & 10 deletions

File tree

server/routes/discovery.js

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1929,22 +1929,28 @@ router.get('/api/discovery/trust-leaderboard', (_req, res) => {
19291929

19301930
// Helper: compute the latest composite WAB Score for a domain.
19311931
// Score is a weighted blend of:
1932-
// - signature/trust integrity (40%) ← from discovery_trust_runs
1933-
// - execution success rate (25%) ← from discovery_usage_runs
1934-
// - latency (lower is better) (15%)
1935-
// - readiness rate (10%)
1936-
// - sample volume bonus (10%) (cold domains are penalised)
1932+
// - signature/trust integrity (30%) ← discovery_trust_runs
1933+
// - execution success rate (20%) ← discovery_usage_runs
1934+
// - latency (lower is better) (10%)
1935+
// - readiness rate ( 8%)
1936+
// - sample volume bonus ( 7%)
1937+
// - configured fairness (15%) ← sites.config (neutrality)
1938+
// - configured security signals (10%) ← sites.config (perms/restrictions/logging)
1939+
// Components with no data shrink the max so cold-start sites aren't
1940+
// double-penalised purely for lack of usage history.
19371941
function computeWabScore(domain) {
19381942
const out = {
19391943
domain,
19401944
score: 0,
19411945
label: 'unrated',
19421946
components: {
1943-
trust: { score: 0, weight: 40, available: false },
1944-
success: { score: 0, weight: 25, available: false },
1945-
latency: { score: 0, weight: 15, available: false, ms: null },
1946-
readiness: { score: 0, weight: 10, available: false },
1947-
volume: { score: 0, weight: 10, runs: 0 },
1947+
trust: { score: 0, weight: 30, available: false },
1948+
success: { score: 0, weight: 20, available: false },
1949+
latency: { score: 0, weight: 10, available: false, ms: null },
1950+
readiness: { score: 0, weight: 8, available: false },
1951+
volume: { score: 0, weight: 7, runs: 0 },
1952+
fairness: { score: 0, weight: 15, available: false },
1953+
security: { score: 0, weight: 10, available: false, signals: [] },
19481954
},
19491955
error_classes: {},
19501956
signature_valid_rate: 0,
@@ -1954,6 +1960,7 @@ function computeWabScore(domain) {
19541960
cold_start: false,
19551961
last_seen: null,
19561962
last_trust_check: null,
1963+
site_registered: false,
19571964
};
19581965

19591966
// --- Trust component (latest run) ---
@@ -2046,6 +2053,46 @@ function computeWabScore(domain) {
20462053
out.cold_start = true;
20472054
}
20482055

2056+
// --- Configuration-based components: fairness + security ---
2057+
// Pulled from sites.config (set by the site owner). Independent of
2058+
// behavioral telemetry; available even for brand-new registrations.
2059+
let siteRow = null;
2060+
try {
2061+
siteRow = db.prepare(
2062+
`SELECT * FROM sites WHERE LOWER(REPLACE(domain, 'www.', '')) = ? AND active = 1`
2063+
).get(domain);
2064+
} catch { /* sites table may not exist in some test setups */ }
2065+
2066+
if (siteRow) {
2067+
out.site_registered = true;
2068+
2069+
// Fairness / neutrality (calculateNeutralityScore returns 0–100 or {score})
2070+
try {
2071+
const fr = calculateNeutralityScore(siteRow);
2072+
const fScore = typeof fr === 'number' ? fr : Number((fr && fr.score) || 0);
2073+
if (Number.isFinite(fScore) && fScore > 0) {
2074+
out.components.fairness.score = Math.max(0, Math.min(100, fScore));
2075+
out.components.fairness.available = true;
2076+
}
2077+
} catch { /* fairness module optional */ }
2078+
2079+
// Security configuration signals from sites.config
2080+
try {
2081+
let cfg = {};
2082+
try { cfg = siteRow.config ? JSON.parse(siteRow.config) : {}; } catch { cfg = {}; }
2083+
let sec = 60; // baseline for a registered site
2084+
const sigs = [];
2085+
if (cfg.agentPermissions) { sec += 15; sigs.push('agent_permissions_configured'); }
2086+
if (cfg.restrictions && Object.keys(cfg.restrictions).length) { sec += 10; sigs.push('restrictions_defined'); }
2087+
if (cfg.logging) { sec += 8; sigs.push('logging_enabled'); }
2088+
if (cfg.rateLimit) { sec += 5; sigs.push('rate_limit_set'); }
2089+
if (cfg.requireAuth) { sec += 5; sigs.push('auth_required'); }
2090+
out.components.security.score = Math.max(0, Math.min(100, sec));
2091+
out.components.security.signals = sigs;
2092+
out.components.security.available = true;
2093+
} catch { /* best-effort */ }
2094+
}
2095+
20492096
// --- Composite weighted score ---
20502097
// Components with data contribute their weight; missing components
20512098
// shrink the maximum so a brand-new but well-signed domain isn't
@@ -2297,4 +2344,5 @@ module.exports._internals = {
22972344
pickUsageAction,
22982345
resolveAbsoluteUrl,
22992346
buildActionParams,
2347+
computeWabScore,
23002348
};

0 commit comments

Comments
 (0)