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
28 changes: 28 additions & 0 deletions public/sample-trust-attestation.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"_sample": "This is a SAMPLE attestation for evaluation purposes. The signature and key id are placeholders and will NOT verify. Generate a real one with `npm run trust:attest` and verify with `npm run trust:verify`.",
"schema_version": 1,
"attested_at": "2026-07-01T12:00:00.000Z",
"package": {
"type": "skill",
"slug": "code-reviewer",
"version": "1.4.2",
"file": "code-reviewer.yaml"
},
"content_sha256": "9f2b1c4a7e8d3f60b5a2c9d1e4f7a0b3c6d9e2f5a8b1c4d7e0f3a6b9c2d5e8f1",
"adversarial": {
"total": 42,
"passed": 41,
"failed": 1,
"pass_rate": 0.976,
"severity_weighted_score": 0.958,
"by_severity": {
"low": { "total": 10, "passed": 10 },
"medium": { "total": 14, "passed": 14 },
"high": { "total": 12, "passed": 11 },
"critical": { "total": 6, "passed": 6 }
}
},
"signing_key_id": "SAMPLE0000000000",
"algorithm": "ed25519",
"signature": "SAMPLE-SIGNATURE-NOT-VALID-base64-placeholder-do-not-use-in-production=="
}
1 change: 1 addition & 0 deletions src/components/site/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export function Footer() {
{ label: "Marketplace", to: "/marketplace" },
{ label: "Packs", to: "/packs" },
{ label: "Pricing", to: "/pricing" },
{ label: "Enterprise", to: "/enterprise" },
{ label: "Docs", to: "/docs" },
]} />
<FooterCol title="Creators" links={[
Expand Down
28 changes: 28 additions & 0 deletions src/lib/marketplace/list.functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ export type MarketplaceItem = {
rating_human_avg: number;
rating_agent_count: number;
rating_agent_avg: number;
/** Trust Score 0..1 from package_trust_scores; null when never scored. */
trust_score: number | null;
/** Evidence-gated verification flag from package_trust_scores. */
trust_verified: boolean;
};

type ListPayload = { items: MarketplaceItem[]; verticals: string[] };
Expand Down Expand Up @@ -92,11 +96,33 @@ async function fetchMarketplace(): Promise<ListPayload> {
}
}

// Trust scores per package (single batched query, keyed by package id).
// Fail soft: on any error every package renders as "Unscored".
const trustByPkg = new Map<string, { score: number | null; verified: boolean }>();
if (ids.length) {
try {
const { data: ts } = await supabaseAdmin
.from("package_trust_scores")
.select("package_id, score, verified")
.in("package_id", ids);
for (const t of ts ?? []) {
const score = t.score == null ? null : Number(t.score);
trustByPkg.set(t.package_id, {
score: score != null && Number.isFinite(score) ? score : null,
verified: Boolean(t.verified),
});
}
} catch {
/* trust data unavailable → leave map empty */
}
}

const round2 = (n: number) => Math.round(n * 100) / 100;
const items: MarketplaceItem[] = (pkgs ?? []).map((p) => {
const rt =
ratingByPkg.get(p.id) ??
{ sum: 0, count: 0, humanSum: 0, humanCount: 0, agentSum: 0, agentCount: 0 };
const trust = trustByPkg.get(p.id);
return {
...p,
type: p.type as MarketplaceItem["type"],
Expand All @@ -108,6 +134,8 @@ async function fetchMarketplace(): Promise<ListPayload> {
rating_human_avg: rt.humanCount ? round2(rt.humanSum / rt.humanCount) : 0,
rating_agent_count: rt.agentCount,
rating_agent_avg: rt.agentCount ? round2(rt.agentSum / rt.agentCount) : 0,
trust_score: trust?.score ?? null,
trust_verified: trust?.verified ?? false,
};
});

Expand Down
48 changes: 48 additions & 0 deletions src/lib/marketplace/trust-tiers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Trust tiers for catalog curation.
//
// Maps a package's Trust Score (0..1, from package_trust_scores) plus its
// evidence-gated `verified` flag into a small set of browse-facing tiers.
// Thresholds are NOT invented here: they reuse the public badgeColor() bands
// defined in @/lib/trust/score (green >= 0.85, yellow >= 0.70, orange >= 0.50)
// and the verification gate computed server-side by recompute_trust_scores_v2
// (MIN_ADVERSARIAL_RUNS_FOR_VERIFIED / MIN_ADVERSARIAL_CASES_FOR_VERIFIED in
// @/lib/trust/scoring). Pure module — safe to import from client code.

import { badgeColor } from "@/lib/trust/score";

export type TrustTier = "verified" | "high" | "baseline" | "low" | "unscored";

export function trustTier(score: number | null | undefined, verified: boolean): TrustTier {
if (score == null || Number.isNaN(score)) return "unscored";
if (verified) return "verified";
const color = badgeColor(score);
if (color === "green" || color === "yellow") return "high"; // >= 0.70
if (color === "orange") return "baseline"; // >= 0.50
return "low"; // < 0.50
}

export const TRUST_TIER_LABELS: Record<TrustTier, string> = {
verified: "Verified",
high: "High trust",
baseline: "Baseline",
low: "Low trust",
unscored: "Unscored",
};

// Lower = better. Used by the "Trust score" sort and by ranking demotion.
export const TRUST_TIER_RANK: Record<TrustTier, number> = {
verified: 0,
high: 1,
baseline: 2,
low: 3,
unscored: 4,
};

/**
* True when a package should be demoted in the default browse sort:
* unscored, or scored below the Baseline band (score < 0.50).
* Nothing is hidden — demoted packages simply sort after scored ones.
*/
export function isTrustDemoted(tier: TrustTier): boolean {
return tier === "low" || tier === "unscored";
}
Loading
Loading