AntFleet

Anatomy · be39e8a7-0

Use of Array.prototype.toSorted may break on Node 18 environments (SSR route + script)

mediummaintainabilityclosed in a58382a
repo e24ef98c·PR #10·reviewed 1 week ago·closed 1 week ago

The vulnerable code

apps/web/app/receipts.rss/route.ts:0-0

0import { loadPublicReceiptsPage, type PublicReceiptRow } from "@/db/queries";
1import {
2 loadCrossRepoReceipts,
3 type CrossRepoReceiptRow,
4} from "@/lib/outgoing-prs";
5import { renderRssFeed, type RssItem } from "@/lib/rss";
6
7// RSS 2.0 feed for the public receipt corpus. Consumed by monitoring tools
8// (Pingdom, BetterStack), RSS readers, and Slack's /feed integration. The
9// feed is the same gate as the /receipts page — only reviews with
10// public_receipt = true reach this surface, and aggregates count blind
11// across all installs but the per-item stream is opt-in only.
12// Cross-repo receipts (outgoing PRs that upstream owners merged) are
13// interleaved by pubDate with a <category>cross-repo</category> tag so
14// subscribers can filter — same trust surface but different artifact
15// shape, and the consumer should know which they're looking at.
16export const runtime = "nodejs";
17export const dynamic = "force-dynamic";
18
19const FEED_LIMIT = 50;
20const CROSS_REPO_LIMIT = 20;
21const SITE_URL = "https://www.antfleet.dev";
22
23export async function GET(): Promise<Response> {
24 const [{ recent, lastUpdatedAt }, crossRepo] = await Promise.all([
25 loadPublicReceiptsPage({ limit: FEED_LIMIT }),
26 loadCrossRepoReceipts(CROSS_REPO_LIMIT),
27 ]);
28
29 const sameRepoItems: RssItem[] = recent
30 .filter((r): r is PublicReceiptRow & { closedAt: Date } => r.closedAt !== null)
31 .map((r) => ({
32 title: `${r.category} · ${r.severity} — ${r.title}`,
33 link: `${SITE_URL}/receipts/${encodeURIComponent(r.findingId)}`,
34 guid: r.findingId,
35 pubDate: r.closedAt,
36 description: buildDescription(r),
37 }));
38
39 const crossRepoItems: RssItem[] = crossRepo.recent.map((r) => ({
40 title: `cross-repo · AntFleet → ${r.upstreamOwner}/${r.upstreamRepo} PR #${r.upstreamPrNumber}`,
41 link: r.prUrl,
42 // guid prefixed with `cross-repo:` so it can never collide with a
43 // same-repo finding_id even if both happen to share the same uuid prefix.
44 guid: `cross-repo:${r.id}`,
45 pubDate: r.mergedAt,
46 description: buildCrossRepoDescription(r),
47 category: "cross-repo",
48 }));
49
50 const items = [...sameRepoItems, ...crossRepoItems].toSorted(
51 (a, b) => b.pubDate.getTime() - a.pubDate.getTime(),
52 );
53 const newestCrossRepo = crossRepo.lastMergedAt;
54 const newestSameRepo = lastUpdatedAt;
55 const lastBuildDate =
56 newestSameRepo === null
57 ? (newestCrossRepo ?? new Date())
58 : newestCrossRepo === null
59 ? newestSameRepo
60 : newestSameRepo > newestCrossRepo
61 ? newestSameRepo
62 : newestCrossRepo;
63
64 const xml = renderRssFeed({
65 title: "AntFleet · Receipts",
66 link: `${SITE_URL}/receipts`,
67 description:
68 "Public, SHA-pinned closure receipts from the AntFleet agreement gate. Every entry is third-party-witnessed on GitHub.",
69 selfLink: `${SITE_URL}/receipts.rss`,
70 lastBuildDate,
71 items,
72 });
73
74 return new Response(xml, {
75 status: 200,
76 headers: {
77 "Content-Type": "application/rss+xml; charset=utf-8",
78 "Cache-Control": "public, max-age=300, s-maxage=300",
79 },
80 });
81}
82
83function buildDescription(row: PublicReceiptRow): string {
84 const parts: string[] = [];
85 parts.push(`repo ${row.repoHash.slice(0, 8)}`);
86 parts.push(`PR #${row.prNumber}`);
87 if (row.closureSha !== null) {
88 parts.push(`closed in ${row.closureSha.slice(0, 7)}`);
89 }
90 return parts.join(" · ");
91}
92
93function buildCrossRepoDescription(row: CrossRepoReceiptRow): string {
94 return [
95 `AntFleet → ${row.upstreamOwner}/${row.upstreamRepo}`,
96 `PR #${row.upstreamPrNumber}`,
97 `merged at ${row.mergeSha.slice(0, 7)}`,
98 ].join(" · ");
99}
100

The reasoning

Opus

Output unavailable for this row.

GPT-5

Use of Array.prototype.toSorted may break on Node 18 environments (SSR route + script)

mediummaintainabilityhigh
  • apps/web/app/receipts.rss/route.ts
  • apps/web/scripts/backfill-benchmark-flag.ts
Array.prototype.toSorted is only available in newer runtimes (Node 20+). The project targets Node server runtimes and local scripts; using toSorted without a polyfill or engine constraint can cause runtime errors in CI/local dev environments on Node 18, and Next.js projects are often executed with varying Node versions.

Recommendation

Replace toSorted with a stable alternative that works on Node 18+, e.g.: - For arrays: use [...arr].sort(comparator) or arr.slice().sort(comparator). - Or add an engines constraint ("engines": { "node": ">=20" }) and enforce it in CI, plus document the requirement. If keeping toSorted, add a polyfill or a small utility function for sorted copies to avoid relying on newer built-ins.

The agreement

Both frontier models flagged this within the same line range. AntFleet's unanimous gate fired — the finding posted on the PR. Closed in a58382a.

The fix

0import { loadPublicReceiptsPage, type PublicReceiptRow } from "@/db/queries";
1import {
2 loadCrossRepoReceipts,
3 type CrossRepoReceiptRow,
4} from "@/lib/receipts";
5import { renderRssFeed, type RssItem } from "@/lib/rss";
6
7// RSS 2.0 feed for the public receipt corpus. Consumed by monitoring tools
8// (Pingdom, BetterStack), RSS readers, and Slack's /feed integration. The
9// feed is the same gate as the /receipts page — only reviews with
10// public_receipt = true reach this surface, and aggregates count blind
11// across all installs but the per-item stream is opt-in only.
12// Cross-repo receipts (outgoing PRs that upstream owners merged) are
13// interleaved by pubDate with a <category>cross-repo</category> tag so
14// subscribers can filter — same trust surface but different artifact
15// shape, and the consumer should know which they're looking at.
16export const runtime = "nodejs";
17export const dynamic = "force-dynamic";
18
19const FEED_LIMIT = 50;
20const CROSS_REPO_LIMIT = 20;
21const SITE_URL = "https://www.antfleet.dev";
22
23export async function GET(): Promise<Response> {
24 const [{ recent, lastUpdatedAt }, crossRepo] = await Promise.all([
25 loadPublicReceiptsPage({ limit: FEED_LIMIT }),
26 loadCrossRepoReceipts(CROSS_REPO_LIMIT),
27 ]);
28
29 const sameRepoItems: RssItem[] = recent
30 .filter((r): r is PublicReceiptRow & { closedAt: Date } => r.closedAt !== null)
31 .map((r) => ({
32 title: `${r.category} · ${r.severity} — ${r.title}`,
33 link: `${SITE_URL}/receipts/${encodeURIComponent(r.findingId)}`,
34 guid: r.findingId,
35 pubDate: r.closedAt,
36 description: buildDescription(r),
37 }));
38
39 const crossRepoItems: RssItem[] = crossRepo.recent.map((r) => ({
40 title: `cross-repo · AntFleet → ${r.upstreamOwner}/${r.upstreamRepo} PR #${r.upstreamPrNumber}`,
41 link: r.prUrl,
42 // guid prefixed with `cross-repo:` so it can never collide with a
43 // same-repo finding_id even if both happen to share the same uuid prefix.
44 guid: `cross-repo:${r.id}`,
45 pubDate: r.mergedAt,
46 description: buildCrossRepoDescription(r),
47 category: "cross-repo",
48 }));
49
50 const items = [...sameRepoItems, ...crossRepoItems].toSorted(
51 (a, b) => b.pubDate.getTime() - a.pubDate.getTime(),
52 );
53 const newestCrossRepo = crossRepo.lastMergedAt;
54 const newestSameRepo = lastUpdatedAt;
55 const lastBuildDate =
56 newestSameRepo === null
57 ? (newestCrossRepo ?? new Date())
58 : newestCrossRepo === null
59 ? newestSameRepo
60 : newestSameRepo > newestCrossRepo
61 ? newestSameRepo
62 : newestCrossRepo;
63
64 const xml = renderRssFeed({
65 title: "AntFleet · Receipts",
66 link: `${SITE_URL}/receipts`,
67 description:
68 "Public, SHA-pinned closure receipts from the AntFleet agreement gate. Every entry is third-party-witnessed on GitHub.",
69 selfLink: `${SITE_URL}/receipts.rss`,
70 lastBuildDate,
71 items,
72 });
73
74 return new Response(xml, {
75 status: 200,
76 headers: {
77 "Content-Type": "application/rss+xml; charset=utf-8",
78 "Cache-Control": "public, max-age=300, s-maxage=300",
79 },
80 });
81}
82
83function buildDescription(row: PublicReceiptRow): string {
84 const parts: string[] = [];
85 parts.push(`repo ${row.repoHash.slice(0, 8)}`);
86 parts.push(`PR #${row.prNumber}`);
87 if (row.closureSha !== null) {
88 parts.push(`closed in ${row.closureSha.slice(0, 7)}`);
89 }
90 return parts.join(" · ");
91}
92
93function buildCrossRepoDescription(row: CrossRepoReceiptRow): string {
94 return [
95 `AntFleet → ${row.upstreamOwner}/${row.upstreamRepo}`,
96 `PR #${row.upstreamPrNumber}`,
97 `merged at ${row.mergeSha.slice(0, 7)}`,
98 ].join(" · ");
99}
100

Closure

Closed 1 week ago

SHA: a58382a1c8934544d327ad62fd4c9c54b187d8ef

View closure receipt on GitHub →

Tweet thread template

tweet 1 of 8181 / 280

Two frontier models reviewed PR #10 on e24ef98c. Both found this bug: medium maintainability: Use of Array.prototype.toSorted may break on Node 18 environments (SSR route + script)

tweet 2 of 8124 / 280

The vulnerable code (apps/web/app/receipts.rss/route.ts:0-0): (full snippet at https://www.antfleet.dev/anatomy/be39e8a7-0)

tweet 3 of 836 / 280

What Opus saw: "Output unavailable"

tweet 4 of 8280 / 280

What GPT-5 saw: "Array.prototype.toSorted is only available in newer runtimes (Node 20+). The project targets Node server runtimes and local scripts; using toSorted without a polyfill or engine constraint can cause runtime errors in CI/local dev environments on Node 18, and Nex…

tweet 5 of 897 / 280

Both flagged the same line range. AntFleet's unanimous gate fired — the finding posted on the PR.

tweet 6 of 893 / 280

The fix landed in commit a58382a: (view diff at https://www.antfleet.dev/anatomy/be39e8a7-0)

tweet 7 of 881 / 280

AntFleet reviews every PR with two frontier models. Only unanimous findings post.

tweet 8 of 877 / 280

Full anatomy + reasoning + diffs: https://www.antfleet.dev/anatomy/be39e8a7-0

Paste into X composer one tweet at a time. X has no multi-tweet intent API.