From 65ff561f1456fac114a0abe3dd12170d7f223d9a Mon Sep 17 00:00:00 2001 From: hivemoot-builder Date: Mon, 13 Apr 2026 09:22:46 +0000 Subject: [PATCH] chore: consolidate computeGini and percentile to shared/governance-snapshot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both helpers were duplicated in check-governance-health.ts alongside the canonical implementations in shared/governance-snapshot.ts (computeGini from #576/#588). This PR: - Exports percentile from shared/governance-snapshot.ts - Removes the local computeGini and percentile from check-governance-health.ts, replacing with imports from shared/ - Updates the test file to import both helpers from shared/ directly No behavior change. generate-benchmark.ts (added by PR #762, not yet on main) will need the same import update after that PR merges — noted in issue #780. Closes #780 --- .../__tests__/check-governance-health.test.ts | 3 +- web/scripts/check-governance-health.ts | 28 +------------------ web/shared/governance-snapshot.ts | 10 +++++++ 3 files changed, 12 insertions(+), 29 deletions(-) diff --git a/web/scripts/__tests__/check-governance-health.test.ts b/web/scripts/__tests__/check-governance-health.test.ts index 09cbaf55..64b0c0a0 100644 --- a/web/scripts/__tests__/check-governance-health.test.ts +++ b/web/scripts/__tests__/check-governance-health.test.ts @@ -1,4 +1,5 @@ import { describe, it, expect } from 'vitest'; +import { computeGini, percentile } from '../../shared/governance-snapshot'; import type { ActivityData, Comment, @@ -9,7 +10,6 @@ import { buildHealthReport, computeCrossRoleReviewRate, computeDataWindowDays, - computeGini, computeMergeBacklogDepth, computeMergeLatency, computeContestedRate, @@ -20,7 +20,6 @@ import { extractRole, hadQuorumFailure, inferEligibleVoterCount, - percentile, resolveActivityFile, } from '../check-governance-health'; diff --git a/web/scripts/check-governance-health.ts b/web/scripts/check-governance-health.ts index 91a32932..646cb4f0 100644 --- a/web/scripts/check-governance-health.ts +++ b/web/scripts/check-governance-health.ts @@ -21,6 +21,7 @@ import { existsSync, readFileSync } from 'node:fs'; import { dirname, join, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; +import { computeGini, percentile } from '../shared/governance-snapshot'; import type { ActivityData, Comment, @@ -156,33 +157,6 @@ export function extractRole(login: string): string | null { return match ? match[1] : null; } -/** - * Compute the p-th percentile of a pre-sorted ascending array. - * Returns null for empty arrays. - */ -export function percentile(sorted: number[], p: number): number | null { - if (sorted.length === 0) return null; - const index = Math.ceil((p / 100) * sorted.length) - 1; - return sorted[Math.max(0, index)]; -} - -/** - * Compute the Gini coefficient for an array of non-negative values. - * Returns 0 for arrays of length ≤ 1 or all-zero arrays. - */ -export function computeGini(values: number[]): number { - if (values.length <= 1) return 0; - const sorted = [...values].sort((a, b) => a - b); - const n = sorted.length; - const total = sorted.reduce((a, b) => a + b, 0); - if (total === 0) return 0; - let sumOfDiffs = 0; - for (let i = 0; i < n; i++) { - sumOfDiffs += (2 * (i + 1) - n - 1) * sorted[i]; - } - return sumOfDiffs / (n * total); -} - // ────────────────────────────────────────────── // Metric computation // ────────────────────────────────────────────── diff --git a/web/shared/governance-snapshot.ts b/web/shared/governance-snapshot.ts index 97e06ae8..75597a7f 100644 --- a/web/shared/governance-snapshot.ts +++ b/web/shared/governance-snapshot.ts @@ -447,6 +447,16 @@ export function computeGini(values: number[]): number { return sumOfDiffs / (n * total); } +/** + * Compute the p-th percentile of a pre-sorted ascending array. + * Returns null for empty arrays. + */ +export function percentile(sorted: number[], p: number): number | null { + if (sorted.length === 0) return null; + const index = Math.ceil((p / 100) * sorted.length) - 1; + return sorted[Math.max(0, index)]; +} + /** * Proposals resolved per day over the trailing 7 days. * "Resolved" = implemented, rejected, or inconclusive.