Runs / loan-contract Batch Runs from different design-system versions should not be compared.
The page A screenshot in each theme, as the benchmark took it.
page.tsx As the agent left it; marked lines broke a rule.
Violations 7 counted, under 2 rules. “Not counted” marks a compiler error that restates a design-system violation, “Ignored” one the agent silenced with a comment.
Steps One row per model call, with the tools it asked for. An agent that reads the guidelines before writing shows it here; what a checker or submit_view answered is under its call.
Step Tools Tokens in Tokens out Cost 0 list_files, docs-list 3,756 47 $0.0012 1 read_file, docs-show 587 70 $0.0003 2 docs-show, docs-show 1,141 67 $0.0005 3
e02ef3974528
Output cap The provider's default 4 CardBody,
5 CardDescription,
6 CardHeader,
7 CardTitle,
8 Table,
9 TableBody,
10 TableCell,
11 TableHead,
12 TableHeader,
13 TableRow,
14 } from "@vektos/ds";
15 import { css } from "styled-system/css";
16 import data from "./fixture.json";
17
18 type BadgeTone = "neutral" | "success" | "warning" | "danger";
19
20 const { contract, schedule, documents } = data;
21
22 const plainAmount = new Intl.NumberFormat("en-GB", {
23 minimumFractionDigits: 2,
24 maximumFractionDigits: 2,
25 });
26
27 const currencyAmount = new Intl.NumberFormat("en-GB", {
28 style: "currency",
29 currency: contract.currency,
30 currencyDisplay: "code",
31 });
32
33 const percent = new Intl.NumberFormat("en-GB", {
34 style: "percent",
35 minimumFractionDigits: 2,
36 maximumFractionDigits: 2,
37 });
38
39 const dateFormatter = new Intl.DateTimeFormat("en-GB", {
40 day: "numeric",
41 month: "short",
42 year: "numeric",
43 timeZone: "UTC",
44 });
45
46 /** ISO dates are parsed as UTC midnight so the rendered day never shifts. */
47 const formatDate = (isoDate: string) => dateFormatter.format(new Date(`${isoDate}T00:00:00Z`));
48
49 type StatusMeta = { label: string; tone: BadgeTone };
50
51 const instalmentStatuses: Record<string, StatusMeta> = {
52 paid: { label: "Paid", tone: "success" },
53 due: { label: "Due", tone: "warning" },
54 upcoming: { label: "Upcoming", tone: "neutral" },
55 };
56
57 const instalmentStatus = (status: string): StatusMeta =>
58 instalmentStatuses[status] ?? { label: status, tone: "neutral" };
59
60 const contractStatus = (status: string): StatusMeta => {
61 switch (status) {
62 case "active":
63 return { label: "Active", tone: "success" };
64 case "overdue":
65 return { label: "Overdue", tone: "danger" };
66 case "settled":
67 case "closed":
68 return { label: "Settled", tone: "neutral" };
69 case "draft":
70 return { label: "Draft", tone: "neutral" };
71 default:
72 return { label: status, tone: "neutral" };
73 }
74 };
75
76 export default function LoanContractPage() {
77 const status = contractStatus(contract.status);
78 const paidCount = schedule.filter((instalment) => instalment.status === "paid").length;
79 const nextInstalment =
80 schedule.find((instalment) => instalment.status === "due") ??
81 schedule.find((instalment) => instalment.status === "upcoming");
82 const firstNumber = schedule.at(0)?.number ?? 1;
83 const lastNumber = schedule.at(-1)?.number ?? schedule.length;
84 const signedDocuments = [...documents].sort((a, b) => b.signedAt.localeCompare(a.signedAt));
85
86 const terms: Array<{ label: string; value: string }> = [
87 { label: "Amount borrowed", value: currencyAmount.format(contract.principal) },
88 { label: "Nominal interest rate", value: percent.format(contract.nominalRate) },
89 { label: "APR", value: percent.format(contract.apr) },
90 { label: "Term", value: `${contract.termMonths} months` },
91 { label: "Monthly payment", value: currencyAmount.format(contract.monthlyPayment) },
92 { label: "Start date", value: formatDate(contract.startDate) },
93 ];
94
95 return (
96 <main
97 className={`vektos ${css({
98 display: "flex",
99 flexDirection: "column",
100 gap: "6",
101 px: { base: "4", md: "8" },
102 py: { base: "6", md: "10" },
103 color: "fg.default",
104 })}`}
105 >
106 <header
107 className={css({
108 display: "flex",
109 flexWrap: "wrap",
110 alignItems: "flex-start",
111 justifyContent: "space-between",
112 gap: "4",
113 })}
114 >
115 <div className={css({ display: "flex", flexDirection: "column", gap: "1" })}>
116 <h1 className={css({ fontSize: "2xl", fontWeight: "semibold" })}>{contract.product}</h1>
117 <p className={css({ fontSize: "sm", color: "fg.muted" })}>
118 Contract {contract.number} · {contract.borrower}
119 </p>
120 </div>
121 <Badge tone={status.tone}>{status.label}</Badge>
122 </header>
123
124 <div
125 className={css({
126 display: "grid",
127 gridTemplateColumns: { base: "1fr", lg: "minmax(0, 2fr) minmax(0, 1fr)" },
128 alignItems: "start",
129 gap: "6",
130 })}
131 >
132 <Card>
133 <CardHeader>
134 <CardTitle asChild>
135 <h2>Key terms</h2>
136 </CardTitle>
137 <CardDescription>The conditions agreed when the contract was signed.</CardDescription>
138 </CardHeader>
139 <CardBody>
140 <dl
141 className={css({
142 display: "grid",
143 gridTemplateColumns: { base: "1fr", sm: "repeat(2, minmax(0, 1fr))" },
144 gap: "4",
145 margin: "0",
146 })}
147 >
148 {terms.map((term) => (
149 <div
150 key={term.label}
151 className={css({ display: "flex", flexDirection: "column", gap: "1" })}
152 >
153 <dt className={css({ fontSize: "sm", color: "fg.muted" })}>{term.label}</dt>
154 <dd className={css({ margin: "0", fontSize: "md", fontWeight: "semibold" })}>
155 {term.value}
156 </dd>
157 </div>
158 ))}
159 </dl>
160 </CardBody>
161 </Card>
162
163 <Card>
164 <CardHeader>
165 <CardTitle asChild>
166 <h2>Outstanding balance</h2>
167 </CardTitle>
168 <CardDescription>
169 {paidCount} of {contract.termMonths} instalments paid.
170 </CardDescription>
171 </CardHeader>
172 <CardBody>
173 <div className={css({ display: "flex", flexDirection: "column", gap: "2" })}>
174 <p
175 className={css({
176 fontSize: "3xl",
177 fontWeight: "semibold",
178 fontVariantNumeric: "tabular-nums",
179 })}
180 >
181 {currencyAmount.format(contract.outstanding)}
182 </p>
183 {nextInstalment ? (
184 <p className={css({ fontSize: "sm", color: "fg.muted" })}>
185 Next instalment {currencyAmount.format(nextInstalment.payment)} due{" "}
186 {formatDate(nextInstalment.dueDate)}.
187 </p>
188 ) : null}
189 </div>
190 </CardBody>
191 </Card>
192 </div>
193
194 <Card>
195 <CardHeader>
196 <CardTitle asChild>
197 <h2>Repayment schedule</h2>
198 </CardTitle>
199 <CardDescription>
200 Instalments {firstNumber}–{lastNumber} of {contract.termMonths}, due monthly.
201 </CardDescription>
202 </CardHeader>
203 <Table aria-label="Repayment schedule" className={css({ marginBlockStart: "4" })}>
204 <TableHead>
205 <TableRow>
206 <TableHeader>No.</TableHeader>
207 <TableHeader>Due date</TableHeader>
208 <TableHeader>Status</TableHeader>
209 <TableHeader numeric>Payment ({contract.currency})</TableHeader>
210 <TableHeader numeric>Principal ({contract.currency})</TableHeader>
211 <TableHeader numeric>Interest ({contract.currency})</TableHeader>
212 <TableHeader numeric>Balance after ({contract.currency})</TableHeader>
213 </TableRow>
214 </TableHead>
215 <TableBody>
216 {schedule.map((instalment) => {
217 const meta = instalmentStatus(instalment.status);
218
219 return (
220 <TableRow key={instalment.number}>
221 <TableCell>{instalment.number}</TableCell>
222 <TableCell>{formatDate(instalment.dueDate)}</TableCell>
223 <TableCell>
224 <Badge tone={meta.tone}>{meta.label}</Badge>
225 </TableCell>
226 <TableCell numeric>{plainAmount.format(instalment.payment)}</TableCell>
227 <TableCell numeric>{plainAmount.format(instalment.principal)}</TableCell>
228 <TableCell numeric>{plainAmount.format(instalment.interest)}</TableCell>
229 <TableCell numeric>{plainAmount.format(instalment.remaining)}</TableCell>
230 </TableRow>
231 );
232 })}
233 </TableBody>
234 </Table>
235 </Card>
236
237 <Card>
238 <CardHeader>
239 <CardTitle asChild>
240 <h2>Signed documents</h2>
241 </CardTitle>
242 <CardDescription>{signedDocuments.length} documents on file for this contract.</CardDescription>
243 </CardHeader>
244 <Table aria-label="Signed documents" className={css({ marginBlockStart: "4" })}>
245 <TableHead>
246 <TableRow>
247 <TableHeader>Document</TableHeader>
248 <TableHeader>Signed on</TableHeader>
249 </TableRow>
250 </TableHead>
251 <TableBody>
252 {signedDocuments.map((document) => (
253 <TableRow key={document.name}>
254 <TableCell>{document.name}</TableCell>
255 <TableCell>{formatDate(document.signedAt)}</TableCell>
256 </TableRow>
257 ))}
258 </TableBody>
259 </Table>
260 </Card>
261 </main>
262 );
263 }
264
Use `Array#toSorted()` instead of `Array#sort()`.
4 read_file, read_file 2,149 183 $0.0009
5 docs-show-story, docs-show 333 581 $0.0009
6 write_file 1,170 11,424 $0.0141
7 stories-changed 11,591 132 $0.0037