profileShare

rasmusjy / roundtable

Read-only snapshot

No repository description.

main default branch 181 files Expires Sep 13, 2026, 9:06 AM
masquerade-note.tsx 1,470 bytes
1 'use client';
2
3 import { VenetianMask } from 'lucide-react';
4 import { useMemo } from 'react';
5 import { buildMasqueradeReport } from '@/core/masquerade';
6 import type { CritiqueRecord, Participant } from '@/core/types';
7
8 /**
9 * One-line anonymity audit: how often reviewers correctly guessed who wrote
10 * an answer, versus chance. Near chance means the masks are holding; well
11 * above means models are stylistically fingerprintable in this debate.
12 */
13 export function MasqueradeNote({
14 participants,
15 critiques,
16 }: {
17 participants: Participant[];
18 critiques: CritiqueRecord[];
19 }) {
20 const report = useMemo(() => buildMasqueradeReport(participants, critiques), [participants, critiques]);
21 if (report.verdict === 'insufficient' || report.hitRate === null || report.chanceRate === null) return null;
22
23 const leaking = report.verdict === 'leaking';
24 return (
25 <p
26 className={`flex items-center gap-1.5 text-xs ${
27 leaking ? 'text-amber-600 dark:text-amber-400' : 'text-muted-foreground'
28 }`}
29 >
30 <VenetianMask className="h-3.5 w-3.5 shrink-0" />
31 Anonymity check: reviewers identified authors in {pct(report.hitRate)} of {report.guesses} guesses vs{' '}
32 {pct(report.chanceRate)} chance.{' '}
33 {leaking
34 ? 'Writing style is leaking authorship in this debate; treat critique scores with extra care.'
35 : 'The masks held.'}
36 </p>
37 );
38 }
39
40 function pct(v: number): string {
41 return `${Math.round(v * 100)}%`;
42 }
43