Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 20 additions & 5 deletions platform/lib/queries/org-summary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,7 @@ export function computeAIvsHuman(

// Attribution gap
let totalFlagged = 0;
let gapHuman = 0;
let hasAttributionGap = false;

for (const [, p] of payloads) {
Expand All @@ -416,6 +417,15 @@ export function computeAIvsHuman(
totalBot += dist.BOT ?? 0;
}

// Attribution-gap denominator, kept apart from totalHuman so commitShare
// is unaffected. The engine omits commit_origin_distribution when a repo
// has zero AI/bot commits in-window — a separate gate from
// attribution_gap's own >=3-flagged threshold — so an all-human repo can
// still feed totalFlagged. Falling back to its total_human_commits keeps
// every numerator repo in the denominator (otherwise flaggedPct exceeded
// 100%, seen as "174 of 142" on a 7-day window).
gapHuman += dist?.HUMAN ?? p.attribution_gap?.total_human_commits ?? 0;

// Stabilization
const stabO = p.stabilization_by_origin;
if (stabO?.HUMAN) {
Expand Down Expand Up @@ -466,9 +476,9 @@ export function computeAIvsHuman(

// Attribution gap. The engine omits this field for repos with fewer than
// 3 flagged commits, so its own total_human_commits only covers flagged
// repos — using totalHuman (summed above from every repo's origin
// distribution) as the denominator instead avoids dropping "clean" repos
// out of the percentage entirely.
// repos — using gapHuman (summed above from every repo) as the
// denominator instead avoids dropping "clean" repos out of the
// percentage entirely.
if (p.attribution_gap) {
hasAttributionGap = true;
totalFlagged += p.attribution_gap.flagged_commits;
Expand Down Expand Up @@ -529,9 +539,14 @@ export function computeAIvsHuman(
toolBreakdown,
attributionGap: hasAttributionGap
? {
flaggedPct: totalHuman > 0 ? (totalFlagged / totalHuman) * 100 : 0,
// Clamped defense-in-depth: flagged_commits is a subset of
// total_human_commits by construction, but a withheld attribution
// gate we haven't anticipated feeding totalFlagged without its
// matching human count should degrade to "100%", not nonsense.
flaggedPct:
gapHuman > 0 ? Math.min(100, (totalFlagged / gapHuman) * 100) : 0,
flaggedCommits: totalFlagged,
totalHumanCommits: totalHuman,
totalHumanCommits: gapHuman,
}
: null,
reposWithAI,
Expand Down
63 changes: 63 additions & 0 deletions platform/tests/org-summary.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,69 @@ describe("computeAIvsHuman — attribution gap", () => {
expect(out!.attributionGap!.totalHumanCommits).toBe(9000);
expect(out!.attributionGap!.flaggedPct).toBeCloseTo((3 / 9000) * 100, 5);
});

it("counts an all-human repo's flagged commits toward the denominator even without commit_origin_distribution", () => {
const payloads = new Map<string, ReportMetrics>();
// The engine omits commit_origin_distribution for a repo with zero
// AI/bot-classified commits in-window — a separate gate from
// attribution_gap's own >=3-flagged threshold, so an all-human repo can
// still trip it. Two such repos here, to show the bug was an org-wide
// aggregate effect, not a per-repo invariant violation (flagged_commits
// is always <= total_human_commits within a single repo's own numbers).
payloads.set(
"r1",
payload({
ai_detection_coverage_pct: 0,
commit_origin_distribution: undefined,
attribution_gap: {
flagged_commits: 100,
total_human_commits: 110,
flagged_pct: 91,
avg_loc: 0,
avg_files: 0,
avg_interval_minutes: 0,
},
}),
);
payloads.set(
"r2",
payload({
ai_detection_coverage_pct: 0,
commit_origin_distribution: undefined,
attribution_gap: {
flagged_commits: 74,
total_human_commits: 80,
flagged_pct: 93,
avg_loc: 0,
avg_files: 0,
avg_interval_minutes: 0,
},
}),
);
// Needs at least one repo with AI detected, or computeAIvsHuman bails
// out entirely (reposWithAI === 0).
payloads.set(
"r3",
payload({
ai_detection_coverage_pct: 10,
commit_origin_distribution: { HUMAN: 50, AI_ASSISTED: 5, BOT: 0 },
}),
);

const out = computeAIvsHuman(payloads);
expect(out).not.toBeNull();
// Before the fix: totalHuman only summed r3's dist.HUMAN (50), while
// totalFlagged summed r1+r2 (174) — a 348% "gap". Fixed denominator is
// 110 + 80 + 50 = 240, so the percentage stays under 100.
expect(out!.attributionGap!.totalHumanCommits).toBe(240);
expect(out!.attributionGap!.flaggedCommits).toBe(174);
expect(out!.attributionGap!.flaggedPct).toBeCloseTo((174 / 240) * 100, 5);
expect(out!.attributionGap!.flaggedPct).toBeLessThanOrEqual(100);
// The gap fallback must not leak into commitShare: only r3 has a
// commit_origin_distribution, so the share stays r3's 50/5/0.
expect(out!.commitShare!.human).toBeCloseTo(50 / 55, 5);
expect(out!.commitShare!.ai).toBeCloseTo(5 / 55, 5);
});
});

describe("computePRHealth — by-origin weighting", () => {
Expand Down
Loading