Skip to content

Commit 45ed06b

Browse files
authored
fix(achievements): gate Original Visionary on computed scores (#141)
1 parent a15cc68 commit 45ed06b

2 files changed

Lines changed: 42 additions & 3 deletions

File tree

apps/web/src/components/chat/environment/EnvironmentOriginalityMeterSection.test.tsx

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ import type { GitOriginalityMeterResult } from "@forkara/contracts";
22
import { renderToStaticMarkup } from "react-dom/server";
33
import { describe, expect, it } from "vitest";
44

5-
import { OriginalityMeterReport } from "./EnvironmentOriginalityMeterSection";
5+
import {
6+
isComputedOriginalityResult,
7+
OriginalityMeterReport,
8+
} from "./EnvironmentOriginalityMeterSection";
69

710
function result(overrides: Partial<GitOriginalityMeterResult> = {}): GitOriginalityMeterResult {
811
return {
@@ -71,3 +74,27 @@ describe("OriginalityMeterReport", () => {
7174
expect(html).toContain("Upstream history may apply");
7275
});
7376
});
77+
78+
describe("isComputedOriginalityResult", () => {
79+
it.each([0, 25, 100])("accepts a genuine computed %i%% result", (scorePercent) => {
80+
expect(isComputedOriginalityResult(result({ scorePercent }))).toBe(true);
81+
});
82+
83+
it.each(["missing_upstream", "incomplete_history", "unrelated_history"] as const)(
84+
"rejects the %s provenance state",
85+
(state) => {
86+
expect(isComputedOriginalityResult(result({ state, scorePercent: null }))).toBe(false);
87+
},
88+
);
89+
90+
it("rejects loading and read-error paths that have no result", () => {
91+
expect(isComputedOriginalityResult(undefined)).toBe(false);
92+
expect(isComputedOriginalityResult(null)).toBe(false);
93+
});
94+
95+
it("fails closed for malformed ready scores", () => {
96+
expect(isComputedOriginalityResult(result({ scorePercent: Number.NaN }))).toBe(false);
97+
expect(isComputedOriginalityResult(result({ scorePercent: -1 }))).toBe(false);
98+
expect(isComputedOriginalityResult(result({ scorePercent: 101 }))).toBe(false);
99+
});
100+
});

apps/web/src/components/chat/environment/EnvironmentOriginalityMeterSection.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,20 @@ function unavailableTitle(state: GitOriginalityMeterResult["state"]): string {
4141
}
4242
}
4343

44+
export function isComputedOriginalityResult(
45+
result: GitOriginalityMeterResult | null | undefined,
46+
): boolean {
47+
return (
48+
result?.state === "ready" &&
49+
typeof result.scorePercent === "number" &&
50+
Number.isFinite(result.scorePercent) &&
51+
result.scorePercent >= 0 &&
52+
result.scorePercent <= 100
53+
);
54+
}
55+
4456
export function OriginalityMeterReport({ result }: { result: GitOriginalityMeterResult }) {
45-
const available = result.state === "ready" && result.scorePercent !== null;
57+
const available = isComputedOriginalityResult(result);
4658
const certification = originalityCertification(result);
4759
return (
4860
<div className="space-y-4">
@@ -162,7 +174,7 @@ export function EnvironmentOriginalityMeterSection({
162174
...GIT_EXPENSIVE_READ_RETRY_OPTIONS,
163175
});
164176
useEffect(() => {
165-
if (originalityQuery.data) {
177+
if (isComputedOriginalityResult(originalityQuery.data)) {
166178
recordAchievementEvent({ type: "originality_meter.result" });
167179
}
168180
}, [originalityQuery.data]);

0 commit comments

Comments
 (0)