feat(governance): enforce fail-closed verification, handle shortfall edge cases, pass 23/23 tests
HIFFI QA & Governance Pipeline / governance-tests (push) Successful in 24s

This commit is contained in:
Srilekha committed 2026-10-26 12:48:15 +05:30
1 parent 0b36fff7b1
commit f40143cb69
2 files changed
+134 -18

No files matched your search

+55 -17
View File
@@ -233,25 +233,40 @@ class HIFFIRankingEngine:
return out return out
def apply_publication_eligibility_gates(self, df: pd.DataFrame) -> pd.DataFrame: def apply_publication_eligibility_gates(
self,
df: pd.DataFrame,
return_metadata: bool = False
) -> pd.DataFrame:
""" """
Constructs a publication ranking layer separate from raw mathematical scores: Constructs a publication ranking layer decoupled from raw scores:
- Top 10 requires all 3 platform signals (Tier 3). - Top 10 requires all 3 platforms (Tier 3), strictly verified.
- Top 50 requires at least 2 platform signals (Tier >= 2). - Top 50 requires at least 2 platforms (Tier >= 2), strictly verified.
- Preserves hiffi_xp_score and hiffi_xp_rank unchanged. - Unverified or unindexed Instagram accounts do NOT contribute to evidence tiers.
- Produces consecutive, unique ranks in 'hiffi_xp_published_rank' (1 to N). - Fail-closed: missing verification column evaluates strictly to False.
- Explicitly tracks shortfalls and degrades gracefully without rank gaps or duplicate integers.
""" """
out = df.copy() out = df.copy()
# 1. Determine verified platform presence tier # 1. Strict Signal Verification
has_yt = out["production_hiffi_score"].notna() has_yt = out["production_hiffi_score"].notna()
has_sp = out["spotify_monthly_listeners"].notna() has_sp = out["spotify_monthly_listeners"].notna()
has_ig = out["instagram_followers"].notna()
# Fail-closed Instagram verification check
verified_ig_statuses = {"AUTOMATED_VERIFIED", "MANUAL_VERIFIED"}
if "instagram_verification_status" in out.columns:
has_ig = (
out["instagram_followers"].notna()
& out["instagram_verification_status"].isin(verified_ig_statuses)
)
else:
has_ig = pd.Series(False, index=out.index)
out["platform_evidence_tier"] = ( out["platform_evidence_tier"] = (
has_yt.astype(int) + has_sp.astype(int) + has_ig.astype(int) has_yt.astype(int) + has_sp.astype(int) + has_ig.astype(int)
) )
# 2. Sort the entire pool deterministically by raw composite score # 2. Sort pool deterministically by raw composite score
sort_cols = ["hiffi_xp_score", "production_hiffi_score", "spotify_monthly_listeners"] sort_cols = ["hiffi_xp_score", "production_hiffi_score", "spotify_monthly_listeners"]
present_sort_cols = [c for c in sort_cols if c in out.columns] present_sort_cols = [c for c in sort_cols if c in out.columns]
pool = out.sort_values( pool = out.sort_values(
@@ -260,23 +275,44 @@ class HIFFIRankingEngine:
).copy() ).copy()
assigned_indices = [] assigned_indices = []
shortfall_audit = {"top_10_shortfall": False, "top_50_shortfall": False}
# Pass 1: Select Top 10 from Tier 3 candidates # Pass 1: Top 10 from Tier 3
tier3_candidates = pool[pool["platform_evidence_tier"] == 3] tier3_candidates = pool[pool["platform_evidence_tier"] == 3]
top_10_indices = tier3_candidates.head(10).index.tolist() top_10_indices = tier3_candidates.head(10).index.tolist()
assigned_indices.extend(top_10_indices) assigned_indices.extend(top_10_indices)
# Pass 2: Select Positions 11 to 50 from remaining Tier >= 2 candidates if len(assigned_indices) < min(10, len(pool)):
remaining_pool = pool.drop(index=assigned_indices) shortfall_audit["top_10_shortfall"] = True
tier2_plus_candidates = remaining_pool[remaining_pool["platform_evidence_tier"] >= 2] remaining_pool = pool.drop(index=assigned_indices)
top_50_fill_indices = tier2_plus_candidates.head(40).index.tolist() shortfall = min(10 - len(assigned_indices), len(remaining_pool))
assigned_indices.extend(top_50_fill_indices) backfill = remaining_pool[remaining_pool["platform_evidence_tier"] >= 2].head(shortfall)
assigned_indices.extend(backfill.index.tolist())
# Pass 3: Fill Positions 51+ with all remaining artists by score order # Complete degraded fallback if Tier 2+ is also exhausted
if len(assigned_indices) < min(10, len(pool)):
remaining_pool = pool.drop(index=assigned_indices)
shortfall = min(10 - len(assigned_indices), len(remaining_pool))
assigned_indices.extend(remaining_pool.head(shortfall).index.tolist())
# Pass 2: Positions 11 to 50 from Tier >= 2
remaining_pool = pool.drop(index=assigned_indices)
needed_for_50 = min(40, len(pool) - len(assigned_indices))
tier2_plus_candidates = remaining_pool[remaining_pool["platform_evidence_tier"] >= 2]
top_50_fill = tier2_plus_candidates.head(needed_for_50).index.tolist()
assigned_indices.extend(top_50_fill)
if len(assigned_indices) < min(50, len(pool)):
shortfall_audit["top_50_shortfall"] = True
remaining_pool = pool.drop(index=assigned_indices)
shortfall = min(50 - len(assigned_indices), len(remaining_pool))
assigned_indices.extend(remaining_pool.head(shortfall).index.tolist())
# Pass 3: Positions 51+ with remaining artists by score order
final_remaining = pool.drop(index=assigned_indices) final_remaining = pool.drop(index=assigned_indices)
assigned_indices.extend(final_remaining.index.tolist()) assigned_indices.extend(final_remaining.index.tolist())
# 3. Assign consecutive published ranks without touching raw scores # 3. Assign contiguous published ranks
out["hiffi_xp_published_rank"] = pd.NA out["hiffi_xp_published_rank"] = pd.NA
rank_series = pd.Series( rank_series = pd.Series(
range(1, len(assigned_indices) + 1), range(1, len(assigned_indices) + 1),
@@ -286,4 +322,6 @@ class HIFFIRankingEngine:
out.loc[assigned_indices, "hiffi_xp_published_rank"] = rank_series out.loc[assigned_indices, "hiffi_xp_published_rank"] = rank_series
out["hiffi_xp_published_rank"] = out["hiffi_xp_published_rank"].astype(int) out["hiffi_xp_published_rank"] = out["hiffi_xp_published_rank"].astype(int)
if return_metadata:
return out, shortfall_audit
return out return out
+79 -1
View File
@@ -377,4 +377,82 @@ def test_publication_eligibility_gates(dataset):
# 4. Check ranks are strictly unique and consecutive 1 to N # 4. Check ranks are strictly unique and consecutive 1 to N
ranks = gated["hiffi_xp_published_rank"].sort_values().tolist() ranks = gated["hiffi_xp_published_rank"].sort_values().tolist()
expected_ranks = list(range(1, len(gated) + 1)) expected_ranks = list(range(1, len(gated) + 1))
assert ranks == expected_ranks, "Published ranks contain gaps or duplicates." assert ranks == expected_ranks, "Published ranks contain gaps or duplicates."
def test_publication_gate_blocks_unverified_identity():
"""Confirms that UNCERTAIN_NEEDS_REVIEW status does not count toward evidence tiers."""
engine = HIFFIRankingEngine()
test_df = pd.DataFrame([
{
"name": "Artist Pending Verification",
"production_hiffi_score": 95.0,
"spotify_monthly_listeners": 40_000_000,
"instagram_followers": 20_000_000,
"instagram_verification_status": "UNCERTAIN_NEEDS_REVIEW",
"hiffi_xp_score": 96.0,
"hiffi_xp_rank": 1
},
{
"name": "Artist Fully Verified",
"production_hiffi_score": 90.0,
"spotify_monthly_listeners": 35_000_000,
"instagram_followers": 15_000_000,
"instagram_verification_status": "AUTOMATED_VERIFIED",
"hiffi_xp_score": 91.0,
"hiffi_xp_rank": 2
}
])
gated = engine.apply_publication_eligibility_gates(test_df)
pending_row = gated[gated["name"] == "Artist Pending Verification"].iloc[0]
verified_row = gated[gated["name"] == "Artist Fully Verified"].iloc[0]
assert pending_row["platform_evidence_tier"] == 2
assert verified_row["platform_evidence_tier"] == 3
def test_publication_gate_missing_column_fails_closed():
"""Confirms that omitting the verification column fails closed (gives 0 IG credit)."""
engine = HIFFIRankingEngine()
test_df = pd.DataFrame([
{
"name": "Artist No Status Column",
"production_hiffi_score": 95.0,
"spotify_monthly_listeners": 40_000_000,
"instagram_followers": 20_000_000,
"hiffi_xp_score": 96.0,
"hiffi_xp_rank": 1
}
])
gated = engine.apply_publication_eligibility_gates(test_df)
assert gated.loc[0, "platform_evidence_tier"] == 2
def test_publication_gate_zero_multiplatform_cohort():
"""Confirms an entirely Tier 1 cohort produces consecutive ranks and flags an audited shortfall."""
engine = HIFFIRankingEngine()
records = [
{
"name": f"Solo Artist {i+1}",
"production_hiffi_score": 85.0 - i,
"spotify_monthly_listeners": np.nan,
"instagram_followers": np.nan,
"instagram_verification_status": "UNVERIFIED",
"hiffi_xp_score": 80.0 - i,
"hiffi_xp_rank": i + 1
}
for i in range(25)
]
tier1_df = pd.DataFrame(records)
gated, audit = engine.apply_publication_eligibility_gates(tier1_df, return_metadata=True)
# Must retain unique contiguous ordering 1 to 25
assert gated["hiffi_xp_published_rank"].tolist() == list(range(1, 26))
# All records remain Tier 1
assert (gated["platform_evidence_tier"] == 1).all()
# Shortfall must be explicitly reported
assert audit["top_10_shortfall"] is True
assert audit["top_50_shortfall"] is True