Skip to content

Commit 04c85cc

Browse files
james tooleclaude
authored andcommitted
Add computeTrustTier pure function with full test coverage
Tier 1 = default, Tier 2 = 5+ successes (0 malicious), Tier 3 = 20+ successes (0 malicious). Any malicious forces Tier 1, banned identities stay Tier 1. Failed resolutions are neutral. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent e2f21a2 commit 04c85cc

2 files changed

Lines changed: 60 additions & 1 deletion

File tree

src/reputation.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,23 @@
1-
import type { IdentityStats } from "./types";
1+
import type { IdentityStats, ResolveOutcome } from "./types";
22

33
export function scoreIdentity(stats: IdentityStats): number {
44
return stats.locks * 2 + stats.actions * 3 + stats.successes * 10 - stats.failures * 5 - stats.malicious * 20;
55
}
6+
7+
export type TrustTier = 1 | 2 | 3;
8+
9+
export function computeTrustTier(
10+
history: ResolveOutcome[],
11+
banned: boolean = false,
12+
): TrustTier {
13+
if (banned) return 1;
14+
15+
const hasMalicious = history.some((h) => h === "malicious");
16+
if (hasMalicious) return 1;
17+
18+
const successes = history.filter((h) => h === "success").length;
19+
20+
if (successes >= 20) return 3;
21+
if (successes >= 5) return 2;
22+
return 1;
23+
}

test/reputation.test.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { describe, it, expect } from "vitest";
2+
import { computeTrustTier } from "../src/reputation";
3+
import type { ResolveOutcome } from "../src/types";
4+
5+
function makeHistory(successes: number, failed: number = 0, malicious: number = 0): ResolveOutcome[] {
6+
return [
7+
...Array<ResolveOutcome>(successes).fill("success"),
8+
...Array<ResolveOutcome>(failed).fill("failed"),
9+
...Array<ResolveOutcome>(malicious).fill("malicious"),
10+
];
11+
}
12+
13+
describe("computeTrustTier", () => {
14+
it("new identity (empty history) returns Tier 1", () => {
15+
expect(computeTrustTier([])).toBe(1);
16+
});
17+
18+
it("identity with 0 successes returns Tier 1", () => {
19+
expect(computeTrustTier(makeHistory(0, 3))).toBe(1);
20+
});
21+
22+
it("identity with 4 successes returns Tier 1 (just under threshold)", () => {
23+
expect(computeTrustTier(makeHistory(4))).toBe(1);
24+
});
25+
26+
it("identity with 5 successes returns Tier 2", () => {
27+
expect(computeTrustTier(makeHistory(5))).toBe(2);
28+
});
29+
30+
it("identity with 20 successes returns Tier 3", () => {
31+
expect(computeTrustTier(makeHistory(20))).toBe(3);
32+
});
33+
34+
it("identity with 25 successes and 1 malicious returns Tier 1", () => {
35+
expect(computeTrustTier(makeHistory(25, 0, 1))).toBe(1);
36+
});
37+
38+
it("banned identity stays Tier 1 regardless of successes", () => {
39+
expect(computeTrustTier(makeHistory(30), true)).toBe(1);
40+
});
41+
});

0 commit comments

Comments
 (0)