fix: verify bounded skills.sh Trending activation (#3366)

This commit is contained in:
Patrick Erichsen
2026-07-31 19:46:16 -07:00
committed by GitHub
parent a16ff751bb
commit c83f1711bd
5 changed files with 203 additions and 23 deletions
+4 -16
View File
@@ -6,7 +6,6 @@ import { internalAction, internalMutation, internalQuery } from "./_generated/se
import {
CANONICAL_TRENDING_FIRST_PAGE_SIZE,
CANONICAL_TRENDING_LANE_LIMIT,
CANONICAL_TRENDING_PUBLISHER_CAP,
CANONICAL_TRENDING_RANKING_VERSION,
CANONICAL_TRENDING_WINDOW_HOURS,
blendCanonicalTrendingPools,
@@ -17,7 +16,7 @@ import {
decodeCanonicalTrendingCursor,
encodeCanonicalTrendingCursor,
isFreshExternalTrendingRun,
retainTopCanonicalTrendingCandidates,
retainCanonicalTrendingLaneCandidates,
type CanonicalTrendingMaterializationCandidate,
} from "./lib/canonicalTrending";
import { forEachCanonicalTrendingSourcePage } from "./lib/canonicalTrendingPagination";
@@ -123,11 +122,6 @@ const operationsValidator = v.object({
functionCalls: v.number(),
});
const LANE_DIVERSITY_RESERVE = {
size: CANONICAL_TRENDING_FIRST_PAGE_SIZE,
publisherCap: CANONICAL_TRENDING_PUBLISHER_CAP,
};
export const getNativeSourceBatchInternal = internalQuery({
args: { skillIds: v.array(v.id("skills")) },
handler: async (ctx, args) => {
@@ -856,17 +850,13 @@ export const materializeInternal = internalAction({
}
}
// The fetched batch is capped at 100, so each lane stays within 100 rows of its limit.
nativeCandidates = retainTopCanonicalTrendingCandidates(
nativeCandidates = retainCanonicalTrendingLaneCandidates(
nativeCandidates,
"clawhub-trending",
CANONICAL_TRENDING_LANE_LIMIT,
LANE_DIVERSITY_RESERVE,
);
risingCandidates = retainTopCanonicalTrendingCandidates(
risingCandidates = retainCanonicalTrendingLaneCandidates(
risingCandidates,
"clawhub-rising",
CANONICAL_TRENDING_LANE_LIMIT,
LANE_DIVERSITY_RESERVE,
);
for (const skillId of skillIds) usageBySkill.delete(String(skillId));
};
@@ -997,11 +987,9 @@ export const materializeInternal = internalAction({
const candidate = buildExternalCanonicalTrendingCandidate(digest);
if (candidate) externalCandidates.push(candidate);
}
externalCandidates = retainTopCanonicalTrendingCandidates(
externalCandidates = retainCanonicalTrendingLaneCandidates(
externalCandidates,
"skills-sh-trending",
CANONICAL_TRENDING_LANE_LIMIT,
LANE_DIVERSITY_RESERVE,
);
},
);
+16
View File
@@ -7,6 +7,7 @@ import {
encodeCanonicalTrendingCursor,
isFreshExternalTrendingRun,
retainTopCanonicalTrendingCandidates,
retainCanonicalTrendingLaneCandidates,
sortCanonicalTrendingPools,
type CanonicalTrendingCandidate,
} from "./canonicalTrending";
@@ -220,6 +221,21 @@ describe("canonical Trending ordering", () => {
"gamma",
]);
});
it("keeps the shared diversity reserve in addition to the bounded lane leaders", () => {
const retained = retainCanonicalTrendingLaneCandidates(
Array.from({ length: 1_001 }, (_, index) =>
candidate(`external-${index}`, "skills-sh-trending", {
publisherKey: index === 1_000 ? "beta" : "alpha",
upstreamRank: index + 1,
}),
),
"skills-sh-trending",
);
expect(retained).toHaveLength(1_001);
expect(retained.at(-1)?.identity).toBe("external-1000");
});
});
describe("canonical Trending cursors", () => {
+10
View File
@@ -375,6 +375,16 @@ export function retainTopCanonicalTrendingCandidates<T extends CanonicalTrending
);
}
export function retainCanonicalTrendingLaneCandidates<T extends CanonicalTrendingCandidate>(
candidates: readonly T[],
lane: CanonicalTrendingLane,
) {
return retainTopCanonicalTrendingCandidates(candidates, lane, CANONICAL_TRENDING_LANE_LIMIT, {
size: CANONICAL_TRENDING_FIRST_PAGE_SIZE,
publisherCap: CANONICAL_TRENDING_PUBLISHER_CAP,
});
}
export function sortCanonicalTrendingPools<T extends CanonicalTrendingCandidate>(
pools: CanonicalTrendingPools<T>,
): CanonicalTrendingPools<T> {
+140
View File
@@ -534,6 +534,146 @@ describe("skills.sh mirror visibility operations", () => {
});
});
it("verifies the bounded Trending lane when eligible production rows exceed its limit", async () => {
const t = convexTest(schema, modules);
const now = Date.now();
const rowCount = 1_001;
const { leaderboardRunId, trendingRunId } = await t.run(async (ctx) => {
const createdLeaderboardRunId = await ctx.db.insert(
"skillsShMirrorRuns",
mirrorRun({
snapshotId: "skills-sh:leaderboard:bounded",
sourceView: "leaderboard",
sourceTotal: rowCount,
counts: mirrorRunCounts({
observed: rowCount,
inserted: rowCount,
detailsInserted: rowCount,
}),
startedAt: now - 3_000,
completedAt: now - 2_000,
updatedAt: now - 2_000,
}),
);
const createdTrendingRunId = await ctx.db.insert(
"skillsShMirrorRuns",
mirrorRun({
snapshotId: "skills-sh:trending:bounded",
sourceView: "trending",
sourceTotal: rowCount,
counts: mirrorRunCounts({
observed: rowCount,
trendingJoined: rowCount,
}),
startedAt: now - 1_500,
completedAt: now - 1_000,
updatedAt: now - 1_000,
}),
);
for (let index = 0; index < rowCount; index += 1) {
const slug = `bounded-${index}`;
const owner = `publisher-${index}`;
await ctx.db.insert(
"skillsShMirrorDigests",
digest({
externalId: `${owner}/skills/${slug}`,
owner,
slug,
normalizedSlug: slug,
normalizedSlugFirstToken: slug,
displayName: `Bounded ${index}`,
normalizedDisplayName: `bounded ${index}`,
normalizedDisplayNameFirstToken: "bounded",
searchText: `bounded ${index}`,
sourceUrl: `https://skills.sh/${owner}/skills/${slug}`,
canonicalRepoUrl: `https://github.com/${owner}/skills`,
githubPath: `skills/${slug}`,
lastObservedRunId: createdLeaderboardRunId,
trendingObservedRunId: createdTrendingRunId,
trendingRank: index + 1,
trendingLifetimeInstalls: rowCount - index,
trendingObservedAt: now - 1_000,
}),
);
}
await ctx.db.insert("skillsShMirrorControls", {
key: "global",
enabled: true,
paused: false,
maxRowsPerRun: 50_000,
maxRowsPerBatch: 50,
maxDetailBytes: 65_536,
latestCompletedLeaderboardRunId: createdLeaderboardRunId,
updatedBy: "codex-test",
reason: "CLAW-603 bounded activation verification",
updatedAt: now - 2_000,
});
return {
leaderboardRunId: createdLeaderboardRunId,
trendingRunId: createdTrendingRunId,
};
});
await t.mutation(internal.canonicalTrending.startSnapshotInternal, {
snapshotId: "skills-native-bounded",
generatedAt: now - 1_000,
expiresAt: now + 24 * 60 * 60 * 1_000,
windowStartDay: 40,
windowEndDay: 40,
windowStartHour: 960,
windowEndHour: 983,
});
await t.mutation(internal.canonicalTrending.startNativePoolInternal, {
poolId: "skills-native-bounded",
generatedAt: now - 1_000,
expiresAt: now + 24 * 60 * 60 * 1_000,
windowStartHour: 960,
windowEndHour: 983,
sealedGeneration: 1,
});
await t.mutation(internal.canonicalTrending.finalizeNativePoolInternal, {
poolId: "skills-native-bounded",
completedAt: now - 500,
sourceCounts: { clawhubTrending: 0, clawhubRising: 0 },
operations: { documentsRead: 10, documentsWritten: 2, functionCalls: 3 },
});
await t.mutation(internal.canonicalTrending.finalizeSnapshotInternal, {
snapshotId: "skills-native-bounded",
completedAt: now - 500,
totalItems: 0,
sourceCounts: { clawhubTrending: 0, clawhubRising: 0, skillsShTrending: 0 },
operations: { documentsRead: 10, documentsWritten: 2, functionCalls: 3 },
nativePoolId: "skills-native-bounded",
});
await expect(
t.action(internal.skillsShMirrorVisibility.verifyAndActivateInternal, {
actor: "codex-test",
reason: "CLAW-603 bounded production-sized activation",
confirm: "activate-skills-sh-public-test",
}),
).resolves.toMatchObject({
activated: true,
leaderboard: { sourceTotal: rowCount, accepted: rowCount, rejected: 0 },
trending: { sourceTotal: rowCount, joined: rowCount, missing: 0 },
corpus: {
activationRunAccepted: rowCount,
activationRunTrendingEligible: rowCount,
activationRunTrendingSelected: 1_000,
},
trendingSnapshot: {
sourceCounts: { skillsShTrending: 1_000 },
nativePool: { reused: true },
},
});
const activatedRuns = await t.run(async (ctx) => ({
leaderboard: await ctx.db.get(leaderboardRunId),
trending: await ctx.db.get(trendingRunId),
}));
expect(activatedRuns.leaderboard?.activationSnapshotId).toEqual(expect.any(String));
expect(activatedRuns.leaderboard?.activatedTrendingRunId).toBe(trendingRunId);
});
it("keeps the public gate closed when activation has to build the native pool", async () => {
const t = convexTest(schema, modules);
const now = Date.now();
+33 -7
View File
@@ -4,6 +4,12 @@ import { internal } from "./_generated/api";
import type { Doc } from "./_generated/dataModel";
import type { ActionCtx, MutationCtx } from "./_generated/server";
import { internalAction, internalMutation, internalQuery } from "./functions";
import {
buildExternalCanonicalTrendingCandidate,
type CanonicalTrendingCandidate,
retainCanonicalTrendingLaneCandidates,
} from "./lib/canonicalTrending";
import { isPublicSkillsShMirrorDigest } from "./lib/skillsShMirrorPublic";
import {
assertSkillsShPublicVisibilityMutationAllowed,
isSkillsShMirrorSourceEligible,
@@ -838,7 +844,9 @@ export const getAuditPageInternal = internalQuery({
tombstoned: 0,
activationRunAccepted: 0,
activationRunTrendingEligible: 0,
activationRunTrendingSelected: 0,
};
const activationRunTrendingCandidates: CanonicalTrendingCandidate[] = [];
for (const digest of page.page) {
const sourceEligible = isSkillsShMirrorSourceEligible(digest);
const publicationFlags = skillsShMirrorPublicationFlags(digest);
@@ -870,15 +878,25 @@ export const getAuditPageInternal = internalQuery({
if (
args.trendingRunId !== undefined &&
digest.trendingObservedRunId === args.trendingRunId &&
publicationFlags.publicVisible &&
publicationFlags.installable &&
Number.isSafeInteger(digest.trendingRank) &&
(digest.trendingRank ?? 0) >= 1
isPublicSkillsShMirrorDigest(digest)
) {
counts.activationRunTrendingEligible += 1;
const candidate = buildExternalCanonicalTrendingCandidate(digest);
if (candidate) {
counts.activationRunTrendingEligible += 1;
activationRunTrendingCandidates.push({
identity: candidate.identity,
lane: candidate.lane,
publisherKey: candidate.publisherKey,
installs24h: candidate.installs24h,
bookmarks24h: candidate.bookmarks24h,
createdAt: candidate.createdAt,
updatedAt: candidate.updatedAt,
upstreamRank: candidate.upstreamRank,
});
}
}
}
return { ...page, page: counts };
return { ...page, page: counts, activationRunTrendingCandidates };
},
});
@@ -907,7 +925,9 @@ async function audit(
tombstoned: 0,
activationRunAccepted: 0,
activationRunTrendingEligible: 0,
activationRunTrendingSelected: 0,
};
let activationRunTrendingCandidates: CanonicalTrendingCandidate[] = [];
while (true) {
const result = (await ctx.runQuery(internal.skillsShMirrorVisibility.getAuditPageInternal, {
paginationOpts: { cursor, numItems: MAX_BATCH_SIZE },
@@ -916,10 +936,15 @@ async function audit(
continueCursor: string;
isDone: boolean;
page: typeof counts;
activationRunTrendingCandidates: CanonicalTrendingCandidate[];
};
for (const key of Object.keys(counts) as Array<keyof typeof counts>) {
counts[key] += result.page[key];
}
activationRunTrendingCandidates = retainCanonicalTrendingLaneCandidates(
[...activationRunTrendingCandidates, ...result.activationRunTrendingCandidates],
"skills-sh-trending",
);
batches += 1;
if (counts.total > MAX_ROWS) {
throw new Error(`skills.sh visibility audit exceeded ${MAX_ROWS} rows`);
@@ -927,6 +952,7 @@ async function audit(
if (result.isDone) break;
cursor = result.continueCursor;
}
counts.activationRunTrendingSelected = activationRunTrendingCandidates.length;
return {
ok: true as const,
batches,
@@ -1011,7 +1037,7 @@ export const verifyAndActivateInternal = internalAction({
}
if (
trendingSnapshot.sourceCounts.skillsShTrending !==
corpusAudit.counts.activationRunTrendingEligible
corpusAudit.counts.activationRunTrendingSelected
) {
throw new Error("skills.sh Trending activation snapshot failed source verification");
}