feat: add controlled skills.sh metadata canary (#3217)

This commit is contained in:
Patrick Erichsen
2026-07-21 23:21:19 -07:00
committed by GitHub
parent a9775fc39b
commit ee9fac51cd
12 changed files with 1410 additions and 10 deletions
+192 -1
View File
@@ -10,6 +10,14 @@ vi.mock("../lib/githubAuth", () => ({
buildGitHubApiHeaders: vi.fn(async () => ({ Authorization: "Bearer placeholder" })),
}));
vi.mock("../lib/githubSkillSync", async (importOriginal) => {
const original = await importOriginal<typeof import("../lib/githubSkillSync")>();
return {
...original,
computeGitHubSkillFolderContentHash: vi.fn(original.computeGitHubSkillFolderContentHash),
};
});
vi.mock("./shared", async (importOriginal) => {
const original = await importOriginal<typeof import("./shared")>();
return {
@@ -21,7 +29,9 @@ vi.mock("./shared", async (importOriginal) => {
const { requireAdminOrResponse, requireApiTokenUserOrResponse } = await import("./shared");
const { buildGitHubApiHeaders } = await import("../lib/githubAuth");
const { skillsShCatalogTestV1Handler } = await import("./skillsShCatalogV1");
const { computeGitHubSkillFolderContentHash } = await import("../lib/githubSkillSync");
const { skillsShCatalogTestV1Handler, verifyControlledCanaryGitHubSource } =
await import("./skillsShCatalogV1");
function sha256(value: string) {
return createHash("sha256").update(value).digest("hex");
@@ -374,4 +384,185 @@ describe("skills.sh catalog Test HTTP API", () => {
);
expect(githubFetch).toHaveBeenCalledTimes(1);
});
it("verifies the controlled canary against immutable public GitHub content", async () => {
const skillMarkdown = "# HTML Artifact Chooser\n";
const fileHash = sha256(skillMarkdown);
const contentHash = sha256(`SKILL.md\0${Buffer.byteLength(skillMarkdown)}\0${fileHash}`);
const githubFetch = vi.fn(async (url: string, init?: RequestInit) => {
expect(init?.headers).toMatchObject({ Authorization: "Bearer placeholder" });
if (url.endsWith("/repos/patrick-erichsen/skills")) {
return new Response(
JSON.stringify({
private: false,
full_name: "Patrick-Erichsen/skills",
owner: { id: 20_157_849, login: "Patrick-Erichsen" },
}),
);
}
if (url.includes("/git/commits/")) {
return new Response(
JSON.stringify({
sha: "050daba89f6b6636470add5cb300aac46a412cf8",
tree: { sha: "tree-sha" },
}),
);
}
if (url.endsWith("/git/trees/tree-sha?recursive=1")) {
return new Response(
JSON.stringify({
truncated: false,
tree: [
{
path: "skills/html/SKILL.md",
type: "blob",
sha: "blob-sha",
size: Buffer.byteLength(skillMarkdown),
},
],
}),
);
}
if (url.endsWith("/git/blobs/blob-sha")) {
return new Response(
JSON.stringify({
encoding: "base64",
content: Buffer.from(skillMarkdown).toString("base64"),
}),
);
}
return new Response("not found", { status: 404 });
});
vi.stubGlobal("fetch", githubFetch);
const proof = await verifyControlledCanaryGitHubSource({
fetchImpl: githubFetch as typeof fetch,
checkedAt: "2026-07-22T05:00:00.000Z",
expected: {
externalId: "patrick-erichsen/skills/html",
githubOwnerId: 20_157_849,
owner: "patrick-erichsen",
repo: "skills",
slug: "html",
displayName: "HTML Artifact Chooser",
sourceUrl: "https://www.skills.sh/patrick-erichsen/skills/html",
githubRepoUrl: "https://github.com/Patrick-Erichsen/skills",
githubPath: "skills/html",
githubCommit: "050daba89f6b6636470add5cb300aac46a412cf8",
githubContentHash: contentHash,
sourceContentHash: contentHash,
installs: 0,
},
});
expect(proof).toMatchObject({
authentication: "clawhub-github-authenticated",
fixtureId: "patrick-html-canary-v1",
externalId: "patrick-erichsen/skills/html",
githubOwnerId: 20_157_849,
githubPath: "skills/html",
githubCommit: "050daba89f6b6636470add5cb300aac46a412cf8",
githubContentHash: contentHash,
githubFetches: 4,
});
expect(githubFetch).toHaveBeenCalledTimes(4);
expect(buildGitHubApiHeaders).toHaveBeenCalledWith({
userAgent: "clawhub/skills-sh-catalog-canary",
allowAnonymous: false,
useGitHubApp: false,
});
});
it("passes only server-fetched GitHub verification when starting the controlled canary", async () => {
const expectedContentHash = "a47adb2c1ac33c088f664b5187971b63d2b958a7b9f01516d26005ca941a108f";
vi.mocked(computeGitHubSkillFolderContentHash).mockResolvedValueOnce(expectedContentHash);
vi.stubGlobal(
"fetch",
vi.fn(async (url: string) => {
if (url.endsWith("/repos/patrick-erichsen/skills")) {
return new Response(
JSON.stringify({
private: false,
full_name: "Patrick-Erichsen/skills",
owner: { id: 20_157_849, login: "Patrick-Erichsen" },
}),
);
}
if (url.includes("/git/commits/")) {
return new Response(
JSON.stringify({
sha: "050daba89f6b6636470add5cb300aac46a412cf8",
tree: { sha: "tree-sha" },
}),
);
}
if (url.endsWith("/git/trees/tree-sha?recursive=1")) {
return new Response(
JSON.stringify({
truncated: false,
tree: [
{
path: "skills/html/SKILL.md",
type: "blob",
sha: "blob-sha",
},
],
}),
);
}
if (url.endsWith("/git/blobs/blob-sha")) {
return new Response(
JSON.stringify({
encoding: "base64",
content: Buffer.from("# Server-fetched content\n").toString("base64"),
}),
);
}
return new Response("not found", { status: 404 });
}),
);
const runMutation = vi.fn(async (_ref: unknown, _args: Record<string, unknown>) => ({
runId: "skillsShCatalogRuns:canary",
}));
const ctx = {
runQuery: vi.fn(async () => ({
environment: "test",
deploymentName: "academic-chihuahua-392",
buildSha: "test-sha",
control: {},
})),
runMutation,
} as never;
const request = new Request("https://academic-chihuahua-392.convex.site/api/v1/ops", {
method: "POST",
body: JSON.stringify({
operation: "start-canary",
reason: "CLAW-557 hidden metadata canary",
sourceVerification: {
githubOwnerId: 1,
githubCommit: "client-controlled",
githubContentHash: "client-controlled",
githubCheckedAt: "client-controlled",
githubFetches: 99_999,
},
}),
});
const response = await skillsShCatalogTestV1Handler(ctx, request);
expect(response.status).toBe(200);
expect(runMutation).toHaveBeenCalledOnce();
expect(runMutation.mock.calls[0]?.[1]).toEqual({
fixtureId: "patrick-html-canary-v1",
actor: "catalog-operator",
reason: "CLAW-557 hidden metadata canary",
sourceVerification: {
githubOwnerId: 20_157_849,
githubCommit: "050daba89f6b6636470add5cb300aac46a412cf8",
githubContentHash: expectedContentHash,
githubCheckedAt: expect.any(String),
githubFetches: 4,
},
});
expect(JSON.stringify(runMutation.mock.calls[0]?.[1])).not.toContain("client-controlled");
});
});
+189
View File
@@ -2,21 +2,32 @@ import { internal } from "../_generated/api";
import type { Id } from "../_generated/dataModel";
import type { ActionCtx } from "../_generated/server";
import { buildGitHubApiHeaders } from "../lib/githubAuth";
import { computeGitHubSkillFolderContentHash } from "../lib/githubSkillSync";
import { applyRateLimit } from "../lib/httpRateLimit";
import {
getSkillsShCatalogFixture,
type SkillsShCatalogFixtureRow,
} from "../lib/skillsShCatalogFixtures";
import { json, requireAdminOrResponse, requireApiTokenUserOrResponse, text } from "./shared";
const internalRefs = internal as unknown as {
skillsShCatalog: {
admitRealScansInternal: unknown;
assertFreshGitHubOwnerAssignmentsInternal: unknown;
getRunReconciliationInternal: unknown;
getStagingLiveControlInternal: unknown;
processFixtureBatchInternal: unknown;
processStagingLiveBatchInternal: unknown;
resolveKnownGitHubOwnersInternal: unknown;
rollbackFixtureRunInternal: unknown;
startFixtureRunInternal: unknown;
startStagingLiveRunInternal: unknown;
};
};
const MAX_GITHUB_OWNER_RESOLUTIONS = 500;
const GITHUB_OWNER_RESOLUTION_CONCURRENCY = 8;
const CONTROLLED_CANARY_FIXTURE_ID = "patrick-html-canary-v1";
const MAX_CONTROLLED_CANARY_FILES = 100;
async function runMutationRef<T>(
ctx: ActionCtx,
@@ -124,6 +135,130 @@ async function fetchAuthenticatedGitHubOwners(owners: string[]) {
return resolved;
}
async function fetchGitHubJson(
url: string,
headers: Record<string, string>,
label: string,
fetchImpl: typeof fetch,
) {
const response = await fetchImpl(url, { headers });
if (!response.ok) {
throw new Error(`${label} failed with HTTP ${response.status}`);
}
return (await response.json()) as Record<string, unknown>;
}
function decodeGitHubBlob(payload: Record<string, unknown>, path: string) {
if (payload.encoding !== "base64" || typeof payload.content !== "string") {
throw new Error(`Controlled canary returned invalid GitHub blob content: ${path}`);
}
return decodeBase64(payload.content.replace(/\s+/g, ""));
}
export async function verifyControlledCanaryGitHubSource(options: {
expected?: SkillsShCatalogFixtureRow;
fetchImpl?: typeof fetch;
checkedAt?: string;
}) {
const expected =
options.expected ??
getSkillsShCatalogFixture(CONTROLLED_CANARY_FIXTURE_ID).findByExternalId(
"patrick-erichsen/skills/html",
);
if (!expected || !expected.githubPath || !expected.githubCommit || !expected.githubContentHash) {
throw new Error("Controlled canary fixture lacks immutable GitHub provenance");
}
const fetchImpl = options.fetchImpl ?? fetch;
const headers = await buildGitHubApiHeaders({
userAgent: "clawhub/skills-sh-catalog-canary",
allowAnonymous: false,
useGitHubApp: false,
});
const repository = await fetchGitHubJson(
`https://api.github.com/repos/${encodeURIComponent(expected.owner)}/${encodeURIComponent(expected.repo)}`,
headers,
"Controlled canary GitHub repository lookup",
fetchImpl,
);
const repositoryOwner = asRecord(repository.owner);
if (
repository.private !== false ||
requireString(repository, "full_name").toLowerCase() !==
`${expected.owner}/${expected.repo}`.toLowerCase() ||
requireNumber(repositoryOwner ?? {}, "id") !== expected.githubOwnerId ||
requireString(repositoryOwner ?? {}, "login").toLowerCase() !== expected.owner.toLowerCase()
) {
throw new Error("Controlled canary GitHub repository identity mismatch");
}
const commit = await fetchGitHubJson(
`https://api.github.com/repos/${encodeURIComponent(expected.owner)}/${encodeURIComponent(expected.repo)}/git/commits/${encodeURIComponent(expected.githubCommit)}`,
headers,
"Controlled canary GitHub commit lookup",
fetchImpl,
);
const commitSha = requireString(commit, "sha").toLowerCase();
const commitTree = asRecord(commit.tree);
const treeSha = requireString(commitTree ?? {}, "sha");
if (commitSha !== expected.githubCommit.toLowerCase()) {
throw new Error("Controlled canary GitHub commit mismatch");
}
const tree = await fetchGitHubJson(
`https://api.github.com/repos/${encodeURIComponent(expected.owner)}/${encodeURIComponent(expected.repo)}/git/trees/${encodeURIComponent(treeSha)}?recursive=1`,
headers,
"Controlled canary GitHub tree lookup",
fetchImpl,
);
if (tree.truncated !== false || !Array.isArray(tree.tree)) {
throw new Error("Controlled canary GitHub tree is incomplete");
}
const prefix = `${expected.githubPath.replace(/\/+$/g, "")}/`;
const blobs = tree.tree
.map(asRecord)
.filter(
(entry): entry is Record<string, unknown> =>
entry !== null &&
entry.type === "blob" &&
typeof entry.path === "string" &&
entry.path.startsWith(prefix),
)
.sort((left, right) => String(left.path).localeCompare(String(right.path)));
if (blobs.length < 1 || blobs.length > MAX_CONTROLLED_CANARY_FILES) {
throw new Error("Controlled canary GitHub folder has an invalid file count");
}
const entries: Record<string, Uint8Array> = {};
for (const blob of blobs) {
const path = requireString(blob, "path");
const blobSha = requireString(blob, "sha");
const payload = await fetchGitHubJson(
`https://api.github.com/repos/${encodeURIComponent(expected.owner)}/${encodeURIComponent(expected.repo)}/git/blobs/${encodeURIComponent(blobSha)}`,
headers,
`Controlled canary GitHub blob lookup: ${path}`,
fetchImpl,
);
entries[path] = decodeGitHubBlob(payload, path);
}
const contentHash = await computeGitHubSkillFolderContentHash(entries, expected.githubPath);
if (contentHash !== expected.githubContentHash.toLowerCase()) {
throw new Error("Controlled canary GitHub content hash mismatch");
}
const checkedAt = options.checkedAt ?? new Date().toISOString();
if (Number.isNaN(Date.parse(checkedAt))) {
throw new Error("Controlled canary GitHub checked time is invalid");
}
return {
authentication: "clawhub-github-authenticated" as const,
fixtureId: CONTROLLED_CANARY_FIXTURE_ID,
externalId: expected.externalId,
githubOwnerId: expected.githubOwnerId,
githubRepo: `${expected.owner}/${expected.repo}`,
githubPath: expected.githubPath,
githubCommit: commitSha,
githubContentHash: contentHash,
githubCheckedAt: checkedAt,
githubFetches: 3 + blobs.length,
};
}
async function resolveAuthenticatedGitHubOwners(ctx: ActionCtx, ownersValue: unknown) {
const owners = normalizeGitHubOwners(ownersValue);
const known = await runQueryRef<{
@@ -264,6 +399,60 @@ export async function skillsShCatalogTestV1Handler(ctx: ActionCtx, request: Requ
const body = asRecord(await request.json());
if (!body) return text("Invalid JSON", 400, rate.headers);
const operation = requireString(body, "operation");
if (operation === "verify-canary") {
return json(await verifyControlledCanaryGitHubSource({}), 200, rate.headers);
}
if (operation === "start-canary") {
const verification = await verifyControlledCanaryGitHubSource({});
const sourceVerification = {
githubOwnerId: verification.githubOwnerId,
githubCommit: verification.githubCommit,
githubContentHash: verification.githubContentHash,
githubCheckedAt: verification.githubCheckedAt,
githubFetches: verification.githubFetches,
};
const result = await runMutationRef<{ runId: string }>(
ctx,
internalRefs.skillsShCatalog.startFixtureRunInternal,
{
fixtureId: CONTROLLED_CANARY_FIXTURE_ID,
actor: auth.user.handle,
reason: requireString(body, "reason"),
sourceVerification,
},
);
return json({ ...result, sourceVerification: verification }, 200, rate.headers);
}
if (operation === "process-fixture") {
return json(
await runMutationRef(ctx, internalRefs.skillsShCatalog.processFixtureBatchInternal, {
runId: requireString(body, "runId"),
}),
200,
rate.headers,
);
}
if (operation === "reconcile") {
return json(
await runQueryRef(ctx, internalRefs.skillsShCatalog.getRunReconciliationInternal, {
runId: requireString(body, "runId"),
}),
200,
rate.headers,
);
}
if (operation === "rollback-canary") {
return json(
await runMutationRef(ctx, internalRefs.skillsShCatalog.rollbackFixtureRunInternal, {
runId: requireString(body, "runId"),
actor: auth.user.handle,
reason: requireString(body, "reason"),
confirm: requireString(body, "confirm"),
}),
200,
rate.headers,
);
}
if (operation === "resolve-owners") {
return json(await resolveAuthenticatedGitHubOwners(ctx, body.owners), 200, rate.headers);
}
+30
View File
@@ -9,6 +9,10 @@ export type SkillsShCatalogFixtureRow = {
displayName: string;
sourceUrl: string;
githubRepoUrl: string;
githubPath?: string;
githubCommit?: string;
githubContentHash?: string;
claimPublisherHandle?: string;
sourceContentHash: string;
installs: number;
};
@@ -43,6 +47,23 @@ const AIQ_DEPLOY_V2: SkillsShCatalogFixtureRow = {
installs: 101,
};
const PATRICK_HTML_CANARY: SkillsShCatalogFixtureRow = {
externalId: "patrick-erichsen/skills/html",
githubOwnerId: 20_157_849,
owner: "patrick-erichsen",
repo: "skills",
slug: "html",
displayName: "HTML Artifact Chooser",
sourceUrl: "https://www.skills.sh/patrick-erichsen/skills/html",
githubRepoUrl: "https://github.com/Patrick-Erichsen/skills",
githubPath: "skills/html",
githubCommit: "050daba89f6b6636470add5cb300aac46a412cf8",
githubContentHash: "a47adb2c1ac33c088f664b5187971b63d2b958a7b9f01516d26005ca941a108f",
claimPublisherHandle: "patrick-erichsen",
sourceContentHash: "a47adb2c1ac33c088f664b5187971b63d2b958a7b9f01516d26005ca941a108f",
installs: 0,
};
const FROZEN_ROWS = frozenSnapshot.rows satisfies SkillsShCatalogFixtureRow[];
function fromRows(
@@ -108,6 +129,15 @@ function syntheticDiscoveryRow(index: number): SkillsShCatalogFixtureRow {
}
const FIXTURES = {
"patrick-html-canary-v1": fromRows(
{
snapshotId: "patrick-html-canary-v1",
sourceKind: "fixture",
capturedAt: null,
snapshotCaptureFetches: 0,
},
[PATRICK_HTML_CANARY],
),
"nvidia-small-v1": fromRows(
{
snapshotId: "nvidia-small-v1",
+28
View File
@@ -2788,6 +2788,10 @@ const skillsShCatalogRunCountsValidator = v.object({
updated: v.number(),
unchanged: v.number(),
rejected: v.number(),
newExternal: v.optional(v.number()),
exactNativeMatches: v.optional(v.number()),
routeCollisions: v.optional(v.number()),
claimOpportunities: v.optional(v.number()),
scansPlanned: v.number(),
scansAdmitted: v.number(),
scansCompleted: v.number(),
@@ -2796,6 +2800,7 @@ const skillsShCatalogRunCountsValidator = v.object({
const skillsShCatalogRuns = defineTable({
fixtureId: v.union(
v.literal("patrick-html-canary-v1"),
v.literal("nvidia-small-v1"),
v.literal("nvidia-small-v2"),
v.literal("skills-sh-500-2026-07-21"),
@@ -2811,6 +2816,15 @@ const skillsShCatalogRuns = defineTable({
),
sourceCapturedAt: v.optional(v.string()),
snapshotCaptureFetches: v.number(),
githubVerification: v.optional(
v.object({
ownerId: v.number(),
commit: v.string(),
contentHash: v.string(),
checkedAt: v.string(),
fetches: v.number(),
}),
),
dryRun: v.boolean(),
status: v.union(
v.literal("running"),
@@ -2867,9 +2881,23 @@ const skillsShCatalogEntries = defineTable({
displayName: v.string(),
sourceUrl: v.string(),
githubRepoUrl: v.string(),
githubPath: v.optional(v.string()),
githubCommit: v.optional(v.string()),
githubContentHash: v.optional(v.string()),
sourceContentHash: v.string(),
installs: v.number(),
sourceSnapshotId: v.string(),
reconciliation: v.optional(
v.object({
kind: v.union(v.literal("new"), v.literal("exact-native"), v.literal("route-collision")),
nativeSkillId: v.optional(v.id("skills")),
nativeSlug: v.optional(v.string()),
nativeStatsDownloads: v.optional(v.number()),
claimOpportunity: v.boolean(),
claimPublisherHandle: v.optional(v.string()),
observedAt: v.number(),
}),
),
publicVisible: v.boolean(),
scanStatus: v.union(
v.literal("not-planned"),
+16
View File
@@ -833,6 +833,10 @@ describe("skills.sh catalog overload control plane", () => {
updated: 0,
unchanged: 500,
rejected: 0,
newExternal: 500,
exactNativeMatches: 0,
routeCollisions: 0,
claimOpportunities: 0,
scansPlanned: 0,
scansAdmitted: 0,
scansCompleted: 0,
@@ -858,6 +862,10 @@ describe("skills.sh catalog overload control plane", () => {
updated: 2,
unchanged: 498,
rejected: 0,
newExternal: 500,
exactNativeMatches: 0,
routeCollisions: 0,
claimOpportunities: 0,
scansPlanned: 1,
scansAdmitted: 0,
scansCompleted: 0,
@@ -2091,6 +2099,10 @@ describe("skills.sh catalog overload control plane", () => {
updated: 0,
unchanged: 0,
rejected: 0,
newExternal: 1,
exactNativeMatches: 0,
routeCollisions: 0,
claimOpportunities: 0,
scansPlanned: 1,
scansAdmitted: 1,
scansCompleted: 0,
@@ -2486,6 +2498,10 @@ describe("skills.sh catalog overload control plane", () => {
updated: 0,
unchanged: 0,
rejected: 0,
newExternal: 1,
exactNativeMatches: 0,
routeCollisions: 0,
claimOpportunities: 0,
scansPlanned: 1,
scansAdmitted: 1,
scansCompleted: 0,
+326 -8
View File
@@ -19,6 +19,8 @@ import { enqueueSkillsShCatalogScanRequest } from "./securityScan";
const CONTROL_KEY = "global";
const ENABLE_FIXTURE_CONFIRM = "enable-skills-sh-fixture-control";
const DISABLE_CATALOG_CONFIRM = "disable-skills-sh-catalog";
const ROLLBACK_CONTROLLED_CANARY_CONFIRM = "rollback-skills-sh-controlled-canary";
const CONTROLLED_CANARY_FIXTURE_ID = "patrick-html-canary-v1";
const STATUS_LIMIT = 50;
const MAX_DISCOVERY_ROWS = 20_000;
const MAX_ENTRIES_PER_BATCH = 250;
@@ -29,6 +31,7 @@ const MAX_REAL_TEST_ADMISSIONS = 10;
const MAX_DETERMINISTIC_COMPLETIONS_PER_BATCH = 50;
const fixtureIdValidator = v.union(
v.literal("patrick-html-canary-v1"),
v.literal("nvidia-small-v1"),
v.literal("nvidia-small-v2"),
v.literal("skills-sh-500-2026-07-21"),
@@ -51,9 +54,20 @@ const stagingLiveRowValidator = v.object({
displayName: v.string(),
sourceUrl: v.string(),
githubRepoUrl: v.string(),
githubPath: v.optional(v.string()),
githubCommit: v.optional(v.string()),
githubContentHash: v.optional(v.string()),
claimPublisherHandle: v.optional(v.string()),
sourceContentHash: v.string(),
installs: v.number(),
});
const sourceVerificationValidator = v.object({
githubOwnerId: v.number(),
githubCommit: v.string(),
githubContentHash: v.string(),
githubCheckedAt: v.string(),
githubFetches: v.number(),
});
const scanRequestFileValidator = v.object({
path: v.string(),
size: v.number(),
@@ -109,10 +123,95 @@ function normalizeIdentity(row: SkillsShCatalogFixtureRow) {
repo,
slug,
externalId: `${owner}/${repo}/${slug}`,
githubPath: row.githubPath?.trim(),
githubCommit: row.githubCommit?.trim().toLowerCase(),
githubContentHash: row.githubContentHash?.trim().toLowerCase(),
claimPublisherHandle: row.claimPublisherHandle?.trim().toLowerCase(),
sourceContentHash: row.sourceContentHash.trim().toLowerCase(),
};
}
async function reconcileNativeSkill(
ctx: MutationCtx,
row: ReturnType<typeof normalizeIdentity>,
observedAt: number,
) {
const nativeSkills = (
await ctx.db
.query("skills")
.withIndex("by_slug", (q) => q.eq("slug", row.slug))
.collect()
).filter((skill) => skill.softDeletedAt === undefined);
let reads = 1;
for (const skill of nativeSkills) {
if (
skill.installKind !== "github" ||
!skill.githubSourceId ||
!row.githubPath ||
!row.githubCommit ||
!row.githubContentHash ||
skill.githubPath !== row.githubPath ||
skill.githubCurrentCommit?.toLowerCase() !== row.githubCommit ||
skill.githubCurrentContentHash?.toLowerCase() !== row.githubContentHash
) {
continue;
}
const source = await ctx.db.get(skill.githubSourceId);
reads += 1;
if (source?.repo.trim().toLowerCase() !== `${row.owner}/${row.repo}`) continue;
return {
reads,
reconciliation: {
kind: "exact-native" as const,
nativeSkillId: skill._id,
nativeSlug: skill.slug,
nativeStatsDownloads: skill.statsDownloads ?? skill.stats.downloads,
claimOpportunity: Boolean(row.claimPublisherHandle),
...(row.claimPublisherHandle ? { claimPublisherHandle: row.claimPublisherHandle } : {}),
observedAt,
},
};
}
const collision = nativeSkills[0];
return {
reads,
reconciliation: {
kind: collision ? ("route-collision" as const) : ("new" as const),
...(collision
? {
nativeSkillId: collision._id,
nativeSlug: collision.slug,
nativeStatsDownloads: collision.statsDownloads ?? collision.stats.downloads,
}
: {}),
claimOpportunity: Boolean(row.claimPublisherHandle),
...(row.claimPublisherHandle ? { claimPublisherHandle: row.claimPublisherHandle } : {}),
observedAt,
},
};
}
function incrementReconciliationCounts(
counts: ReturnType<typeof normalizedCounts>,
reconciliation: Doc<"skillsShCatalogEntries">["reconciliation"],
) {
if (!reconciliation) return;
if (reconciliation.kind === "new") counts.newExternal += 1;
if (reconciliation.kind === "exact-native") counts.exactNativeMatches += 1;
if (reconciliation.kind === "route-collision") counts.routeCollisions += 1;
if (reconciliation.claimOpportunity) counts.claimOpportunities += 1;
}
function normalizedCounts(counts: Doc<"skillsShCatalogRuns">["counts"]) {
return {
...counts,
newExternal: counts.newExternal ?? 0,
exactNativeMatches: counts.exactNativeMatches ?? 0,
routeCollisions: counts.routeCollisions ?? 0,
claimOpportunities: counts.claimOpportunities ?? 0,
};
}
async function getControlDoc(ctx: Pick<QueryCtx | MutationCtx, "db">) {
return await ctx.db
.query("skillsShCatalogControls")
@@ -377,18 +476,74 @@ export const startFixtureRunInternal = internalMutation({
actor: v.string(),
reason: v.string(),
dryRun: v.optional(v.boolean()),
sourceVerification: v.optional(sourceVerificationValidator),
},
handler: async (ctx, args) => {
assertSkillsShFixtureEnvironmentAllowed();
const control = assertFixtureMode(assertDiscoveryEnabled(await getControlDoc(ctx)));
const fixture = getSkillsShCatalogFixture(args.fixtureId);
let githubVerification:
| {
ownerId: number;
commit: string;
contentHash: string;
checkedAt: string;
fetches: number;
}
| undefined;
if (args.fixtureId === CONTROLLED_CANARY_FIXTURE_ID) {
if (
control.scanAdmissionEnabled ||
control.publicVisibilityEnabled ||
control.maxEntriesPerRun !== 1 ||
control.maxEntriesPerBatch !== 1 ||
control.maxPlannedScans !== 1 ||
control.maxScanAdmissionsPerBatch !== 0 ||
control.maxScanAdmissionsPerRun !== 0 ||
control.maxScanAdmissionsPerDay !== 0 ||
control.maxCatalogQueued !== 0 ||
control.maxCatalogInFlight !== 0
) {
throw new ConvexError(
"controlled skills.sh canary requires the exact one-row hidden no-admission control",
);
}
const row = normalizeIdentity(fixture.rowAt(0));
const verification = args.sourceVerification;
if (
!verification ||
verification.githubOwnerId !== row.githubOwnerId ||
verification.githubCommit.trim().toLowerCase() !== row.githubCommit ||
verification.githubContentHash.trim().toLowerCase() !== row.githubContentHash ||
!Number.isInteger(verification.githubFetches) ||
verification.githubFetches < 1 ||
Number.isNaN(Date.parse(verification.githubCheckedAt))
) {
throw new ConvexError(
"controlled skills.sh canary requires exact authenticated GitHub source verification",
);
}
githubVerification = {
ownerId: verification.githubOwnerId,
commit: verification.githubCommit.trim().toLowerCase(),
contentHash: verification.githubContentHash.trim().toLowerCase(),
checkedAt: verification.githubCheckedAt,
fetches: verification.githubFetches,
};
} else if (args.sourceVerification) {
throw new ConvexError("GitHub source verification is reserved for the controlled canary");
}
const now = Date.now();
const runId = await ctx.db.insert("skillsShCatalogRuns", {
fixtureId: args.fixtureId,
snapshotId: fixture.snapshotId,
sourceKind: fixture.sourceKind,
...(fixture.capturedAt ? { sourceCapturedAt: fixture.capturedAt } : {}),
snapshotCaptureFetches: fixture.snapshotCaptureFetches,
...(githubVerification
? { sourceCapturedAt: githubVerification.checkedAt, githubVerification }
: fixture.capturedAt
? { sourceCapturedAt: fixture.capturedAt }
: {}),
snapshotCaptureFetches: githubVerification?.fetches ?? fixture.snapshotCaptureFetches,
dryRun: args.dryRun ?? false,
status: "running",
cursor: 0,
@@ -515,7 +670,7 @@ export const processStagingLiveBatchInternal = internalMutation({
let cursor = run.cursor;
let writesUsed = 0;
let readsUsed = 2;
const counts = { ...run.counts };
const counts = normalizedCounts(run.counts);
const now = Date.now();
for (const inputRow of args.rows) {
if (counts.observed >= run.budgets.maxEntriesPerRun) break;
@@ -534,7 +689,11 @@ export const processStagingLiveBatchInternal = internalMutation({
cursor += 1;
continue;
}
const observationUnchanged = existing ? sameFixtureObservation(existing, row) : false;
const native = await reconcileNativeSkill(ctx, row, now);
readsUsed += native.reads;
const observationUnchanged = existing
? sameFixtureObservation(existing, row, native.reconciliation)
: false;
const contentChanged = existing
? existing.sourceContentHash !== row.sourceContentHash
: false;
@@ -559,6 +718,7 @@ export const processStagingLiveBatchInternal = internalMutation({
(!existing || contentChanged || existing.scanStatus !== "planned");
if (writesUsed + 2 > run.budgets.maxWritesPerBatch) break;
incrementReconciliationCounts(counts, native.reconciliation);
counts.observed += 1;
cursor += 1;
if (shouldPlanScan) counts.scansPlanned += 1;
@@ -577,9 +737,13 @@ export const processStagingLiveBatchInternal = internalMutation({
displayName: row.displayName,
sourceUrl: row.sourceUrl,
githubRepoUrl: row.githubRepoUrl,
githubPath: row.githubPath,
githubCommit: row.githubCommit,
githubContentHash: row.githubContentHash,
sourceContentHash: row.sourceContentHash,
installs: row.installs,
sourceSnapshotId: run.snapshotId,
reconciliation: native.reconciliation,
publicVisible: false,
scanStatus: shouldPlanScan
? "planned"
@@ -604,9 +768,13 @@ export const processStagingLiveBatchInternal = internalMutation({
displayName: row.displayName,
sourceUrl: row.sourceUrl,
githubRepoUrl: row.githubRepoUrl,
githubPath: row.githubPath,
githubCommit: row.githubCommit,
githubContentHash: row.githubContentHash,
sourceContentHash: row.sourceContentHash,
installs: row.installs,
sourceSnapshotId: run.snapshotId,
reconciliation: native.reconciliation,
publicVisible: false,
scanStatus: shouldPlanScan ? "planned" : "not-planned",
firstObservedAt: now,
@@ -672,7 +840,7 @@ export const processFixtureBatchInternal = internalMutation({
let writesUsed = 0;
let readsUsed = 2;
let entriesProcessed = 0;
const counts = { ...run.counts };
const counts = normalizedCounts(run.counts);
const now = Date.now();
while (
@@ -693,8 +861,12 @@ export const processFixtureBatchInternal = internalMutation({
entriesProcessed += 1;
continue;
}
const native = await reconcileNativeSkill(ctx, row, now);
readsUsed += native.reads;
const observationUnchanged = existing ? sameFixtureObservation(existing, row) : false;
const observationUnchanged = existing
? sameFixtureObservation(existing, row, native.reconciliation)
: false;
const contentChanged = existing
? existing.sourceContentHash !== row.sourceContentHash
: false;
@@ -720,6 +892,7 @@ export const processFixtureBatchInternal = internalMutation({
const entryWriteRequired = !run.dryRun;
if (entryWriteRequired && writesUsed + 2 > run.budgets.maxWritesPerBatch) break;
incrementReconciliationCounts(counts, native.reconciliation);
counts.observed += 1;
cursor += 1;
entriesProcessed += 1;
@@ -740,9 +913,13 @@ export const processFixtureBatchInternal = internalMutation({
displayName: row.displayName,
sourceUrl: row.sourceUrl,
githubRepoUrl: row.githubRepoUrl,
githubPath: row.githubPath,
githubCommit: row.githubCommit,
githubContentHash: row.githubContentHash,
sourceContentHash: row.sourceContentHash,
installs: row.installs,
sourceSnapshotId: fixture.snapshotId,
reconciliation: native.reconciliation,
// This gate has no publication seam; every catalog write reasserts dark visibility.
publicVisible: false,
scanStatus: shouldPlanScan
@@ -772,9 +949,13 @@ export const processFixtureBatchInternal = internalMutation({
displayName: row.displayName,
sourceUrl: row.sourceUrl,
githubRepoUrl: row.githubRepoUrl,
githubPath: row.githubPath,
githubCommit: row.githubCommit,
githubContentHash: row.githubContentHash,
sourceContentHash: row.sourceContentHash,
installs: row.installs,
sourceSnapshotId: fixture.snapshotId,
reconciliation: native.reconciliation,
publicVisible: false,
scanStatus: shouldPlanScan ? "planned" : "not-planned",
firstObservedAt: now,
@@ -1781,6 +1962,127 @@ export const getRunInternal = internalQuery({
},
});
export const getRunReconciliationInternal = internalQuery({
args: {
runId: v.id("skillsShCatalogRuns"),
},
handler: async (ctx, args) => {
const run = await ctx.db.get(args.runId);
if (!run) throw new ConvexError("skills.sh catalog run not found");
if (run.sourceKind === "staging-live" || run.fixtureId === "skills-sh-test-live-500") {
throw new ConvexError("skills.sh reconciliation readback requires a fixture run");
}
const fixture = getSkillsShCatalogFixture(run.fixtureId);
const entries = [];
const mismatches: string[] = [];
for (let index = 0; index < fixture.length; index += 1) {
const expected = normalizeIdentity(fixture.rowAt(index));
const entry = await ctx.db
.query("skillsShCatalogEntries")
.withIndex("by_external_id", (q) => q.eq("externalId", expected.externalId))
.unique();
if (!entry) {
mismatches.push(`missing:${expected.externalId}`);
continue;
}
if (entry.sourceSnapshotId !== fixture.snapshotId) {
mismatches.push(`snapshot:${expected.externalId}`);
}
if (
entry.githubOwnerId !== expected.githubOwnerId ||
entry.githubPath !== expected.githubPath ||
entry.githubCommit !== expected.githubCommit ||
entry.githubContentHash !== expected.githubContentHash ||
entry.sourceContentHash !== expected.sourceContentHash
) {
mismatches.push(`provenance:${expected.externalId}`);
}
if (entry.publicVisible || !entry.reconciliation) {
mismatches.push(`dark-state:${expected.externalId}`);
}
entries.push({
...entry,
resolution: {
externalRoute: `/skills-sh/${entry.externalId}`,
installRef: `skills-sh:${entry.externalId}`,
installable: false,
},
});
}
return {
run: summarizeRun(run),
reconciled: mismatches.length === 0 && entries.length === fixture.length,
mismatches,
entries,
limits: {
fixtureRows: fixture.length,
maxEntriesPerRun: run.budgets.maxEntriesPerRun,
maxEntriesPerBatch: run.budgets.maxEntriesPerBatch,
maxWritesPerBatch: run.budgets.maxWritesPerBatch,
maxPlannedScans: run.budgets.maxPlannedScans,
maxScanAdmissionsPerRun: run.budgets.maxScanAdmissionsPerRun,
},
};
},
});
export const rollbackFixtureRunInternal = internalMutation({
args: {
runId: v.id("skillsShCatalogRuns"),
actor: v.string(),
reason: v.string(),
confirm: v.string(),
},
handler: async (ctx, args) => {
assertSkillsShFixtureEnvironmentAllowed();
assertFixtureMode(await getControlDoc(ctx));
if (args.confirm !== ROLLBACK_CONTROLLED_CANARY_CONFIRM) {
throw new ConvexError(
`Pass confirm="${ROLLBACK_CONTROLLED_CANARY_CONFIRM}" to roll back the controlled canary.`,
);
}
const run = await ctx.db.get(args.runId);
if (!run) throw new ConvexError("skills.sh catalog run not found");
if (run.fixtureId !== CONTROLLED_CANARY_FIXTURE_ID || run.sourceKind !== "fixture") {
throw new ConvexError("Only the controlled skills.sh canary fixture can be rolled back");
}
const fixture = getSkillsShCatalogFixture(run.fixtureId);
let deletedEntries = 0;
for (let index = 0; index < fixture.length; index += 1) {
const expected = normalizeIdentity(fixture.rowAt(index));
const entry = await ctx.db
.query("skillsShCatalogEntries")
.withIndex("by_external_id", (q) => q.eq("externalId", expected.externalId))
.unique();
if (!entry) continue;
if (entry.sourceKind !== "fixture" || entry.sourceSnapshotId !== fixture.snapshotId) {
throw new ConvexError(`Controlled canary no longer owns ${expected.externalId}`);
}
const attempt = await ctx.db
.query("skillsShCatalogScanAttempts")
.withIndex("by_entry_and_source_content_hash", (q) =>
q.eq("entryId", entry._id).eq("sourceContentHash", entry.sourceContentHash),
)
.filter((q) => q.neq(q.field("status"), "canceled"))
.first();
if (attempt) {
throw new ConvexError(
`Controlled canary has retained scan history: ${expected.externalId}`,
);
}
await ctx.db.delete(entry._id);
deletedEntries += 1;
}
return {
fixtureId: run.fixtureId,
actor: args.actor.trim(),
reason: args.reason.trim(),
deletedEntries,
nativeSkillsChanged: 0,
};
},
});
export const getStagingLiveControlInternal = internalQuery({
args: {},
handler: async (ctx) => {
@@ -1922,6 +2224,10 @@ function emptyCounts() {
updated: 0,
unchanged: 0,
rejected: 0,
newExternal: 0,
exactNativeMatches: 0,
routeCollisions: 0,
claimOpportunities: 0,
scansPlanned: 0,
scansAdmitted: 0,
scansCompleted: 0,
@@ -1932,7 +2238,9 @@ function emptyCounts() {
function sameFixtureObservation(
existing: Doc<"skillsShCatalogEntries">,
row: ReturnType<typeof normalizeIdentity>,
reconciliation: NonNullable<Doc<"skillsShCatalogEntries">["reconciliation"]>,
) {
const existingReconciliation = existing.reconciliation;
return (
existing.githubOwnerId === row.githubOwnerId &&
existing.owner === row.owner &&
@@ -1941,8 +2249,17 @@ function sameFixtureObservation(
existing.displayName === row.displayName &&
existing.sourceUrl === row.sourceUrl &&
existing.githubRepoUrl === row.githubRepoUrl &&
existing.githubPath === row.githubPath &&
existing.githubCommit === row.githubCommit &&
existing.githubContentHash === row.githubContentHash &&
existing.sourceContentHash === row.sourceContentHash &&
existing.installs === row.installs
existing.installs === row.installs &&
existingReconciliation?.kind === reconciliation.kind &&
existingReconciliation.nativeSkillId === reconciliation.nativeSkillId &&
existingReconciliation.nativeSlug === reconciliation.nativeSlug &&
existingReconciliation.nativeStatsDownloads === reconciliation.nativeStatsDownloads &&
existingReconciliation.claimOpportunity === reconciliation.claimOpportunity &&
existingReconciliation.claimPublisherHandle === reconciliation.claimPublisherHandle
);
}
@@ -2163,12 +2480,13 @@ function summarizeRun(run: Doc<"skillsShCatalogRuns">) {
sourceKind: run.sourceKind,
sourceCapturedAt: run.sourceCapturedAt,
snapshotCaptureFetches: run.snapshotCaptureFetches,
githubVerification: run.githubVerification,
dryRun: run.dryRun,
status: run.status,
cursor: run.cursor,
scanCursor: run.scanCursor,
fixtureLength: run.fixtureLength,
counts: run.counts,
counts: normalizedCounts(run.counts),
budgets: run.budgets,
operations: run.operations,
actor: run.actor,
+296
View File
@@ -0,0 +1,296 @@
/// <reference types="vite/client" />
/* @vitest-environment edge-runtime */
import { convexTest } from "convex-test";
import { afterEach, describe, expect, it, vi } from "vitest";
import { internal } from "./_generated/api";
import type { Id } from "./_generated/dataModel";
import schema from "./schema";
const modules = import.meta.glob("./**/*.ts");
const LOCAL_ENV = {
CONVEX_CLOUD_URL: "http://127.0.0.1:3210",
};
const CANARY_EXTERNAL_ID = "patrick-erichsen/skills/html";
const CANARY_COMMIT = "050daba89f6b6636470add5cb300aac46a412cf8";
const CANARY_CONTENT_HASH = "a47adb2c1ac33c088f664b5187971b63d2b958a7b9f01516d26005ca941a108f";
const CANARY_CONTROL = {
actor: "codex-test",
reason: "exercise the controlled hidden metadata canary",
confirm: "enable-skills-sh-fixture-control",
mode: "fixture" as const,
discoveryEnabled: true,
writesEnabled: true,
scanPlanningEnabled: true,
scanAdmissionEnabled: false,
maxEntriesPerRun: 1,
maxEntriesPerBatch: 1,
maxWritesPerBatch: 2,
maxPlannedScans: 1,
maxScanAdmissionsPerBatch: 0,
maxScanAdmissionsPerRun: 0,
maxScanAdmissionsPerDay: 0,
maxCatalogQueued: 0,
maxCatalogInFlight: 0,
maxNativeQueued: 0,
maxNativeInFlight: 0,
realScanAllowlist: [] as string[],
};
const SOURCE_VERIFICATION = {
githubOwnerId: 20_157_849,
githubCommit: CANARY_COMMIT,
githubContentHash: CANARY_CONTENT_HASH,
githubCheckedAt: "2026-07-22T05:00:00.000Z",
githubFetches: 4,
};
type CatalogTest = ReturnType<typeof convexTest>;
function useLocalEnvironment() {
for (const [name, value] of Object.entries(LOCAL_ENV)) vi.stubEnv(name, value);
}
async function configureCanary(t: CatalogTest) {
return await t.mutation(internal.skillsShCatalog.configureFixtureControlInternal, CANARY_CONTROL);
}
async function runCanary(t: CatalogTest) {
const started = await t.mutation(internal.skillsShCatalog.startFixtureRunInternal, {
fixtureId: "patrick-html-canary-v1",
actor: "codex-test",
reason: "apply one controlled hidden metadata canary",
sourceVerification: SOURCE_VERIFICATION,
});
const run = await t.mutation(internal.skillsShCatalog.processFixtureBatchInternal, {
runId: started.runId,
});
return { runId: started.runId, run };
}
async function seedNativeSkill(
t: CatalogTest,
options: {
exactSource: boolean;
downloads: number;
},
) {
return await t.run(async (ctx) => {
const userId = await ctx.db.insert("users", {
handle: "native-owner",
displayName: "Native Owner",
role: "user",
});
let githubSourceId: Id<"githubSkillSources"> | undefined;
if (options.exactSource) {
githubSourceId = await ctx.db.insert("githubSkillSources", {
repo: "Patrick-Erichsen/skills",
lastSyncStatus: "ok",
createdAt: 1,
updatedAt: 1,
});
}
const skillId = await ctx.db.insert("skills", {
slug: "html",
displayName: options.exactSource ? "HTML Artifact Chooser" : "Native HTML",
ownerUserId: userId,
...(githubSourceId
? {
installKind: "github" as const,
githubSourceId,
githubPath: "skills/html",
githubCurrentCommit: CANARY_COMMIT,
githubCurrentContentHash: CANARY_CONTENT_HASH,
githubCurrentStatus: "present" as const,
githubCurrentCheckedAt: 1,
githubScanStatus: "clean" as const,
}
: {}),
tags: {},
moderationStatus: "active",
statsDownloads: options.downloads,
statsStars: 0,
statsInstallsCurrent: 0,
statsInstallsAllTime: 0,
stats: {
downloads: options.downloads,
stars: 0,
installsCurrent: 0,
installsAllTime: 0,
versions: 0,
comments: 0,
},
createdAt: 1,
updatedAt: 1,
});
return skillId;
});
}
describe("skills.sh controlled hidden metadata canary", () => {
afterEach(() => {
vi.unstubAllEnvs();
});
it("records a new external skill without creating native state", async () => {
useLocalEnvironment();
const t = convexTest(schema, modules);
await configureCanary(t);
const { runId, run } = await runCanary(t);
const readback = await t.query(internal.skillsShCatalog.getRunReconciliationInternal, {
runId,
});
expect(run).toMatchObject({
status: "completed",
counts: {
observed: 1,
inserted: 1,
newExternal: 1,
exactNativeMatches: 0,
routeCollisions: 0,
claimOpportunities: 1,
scansPlanned: 1,
scansAdmitted: 0,
},
});
expect(readback).toMatchObject({
reconciled: true,
mismatches: [],
entries: [
{
externalId: CANARY_EXTERNAL_ID,
githubOwnerId: 20_157_849,
githubPath: "skills/html",
githubCommit: CANARY_COMMIT,
githubContentHash: CANARY_CONTENT_HASH,
publicVisible: false,
reconciliation: {
kind: "new",
claimOpportunity: true,
claimPublisherHandle: "patrick-erichsen",
},
resolution: {
installable: false,
},
},
],
});
expect(await t.run(async (ctx) => await ctx.db.query("skills").collect())).toHaveLength(0);
expect(
await t.run(async (ctx) => await ctx.db.query("securityScanJobs").collect()),
).toHaveLength(0);
});
it("records an exact native match and preserves its downloads", async () => {
useLocalEnvironment();
const t = convexTest(schema, modules);
const nativeSkillId = await seedNativeSkill(t, { exactSource: true, downloads: 143 });
await configureCanary(t);
const { runId, run } = await runCanary(t);
const readback = await t.query(internal.skillsShCatalog.getRunReconciliationInternal, {
runId,
});
const native = await t.run(async (ctx) => await ctx.db.get(nativeSkillId));
expect(run.counts).toMatchObject({
newExternal: 0,
exactNativeMatches: 1,
routeCollisions: 0,
});
expect(readback.entries[0]).toMatchObject({
reconciliation: {
kind: "exact-native",
nativeSkillId,
nativeStatsDownloads: 143,
claimOpportunity: true,
},
});
expect(native).toMatchObject({
_id: nativeSkillId,
statsDownloads: 143,
stats: { downloads: 143 },
githubCurrentCommit: CANARY_COMMIT,
githubCurrentContentHash: CANARY_CONTENT_HASH,
});
});
it("records a route collision without changing or attaching the native skill", async () => {
useLocalEnvironment();
const t = convexTest(schema, modules);
const nativeSkillId = await seedNativeSkill(t, { exactSource: false, downloads: 77 });
await configureCanary(t);
const { runId, run } = await runCanary(t);
const readback = await t.query(internal.skillsShCatalog.getRunReconciliationInternal, {
runId,
});
const native = await t.run(async (ctx) => await ctx.db.get(nativeSkillId));
expect(run.counts).toMatchObject({
newExternal: 0,
exactNativeMatches: 0,
routeCollisions: 1,
});
expect(readback.entries[0]).toMatchObject({
reconciliation: {
kind: "route-collision",
nativeSkillId,
nativeStatsDownloads: 77,
claimOpportunity: true,
},
});
expect(native).toMatchObject({
_id: nativeSkillId,
statsDownloads: 77,
stats: { downloads: 77 },
});
expect(native?.ownerPublisherId).toBeUndefined();
});
it("reruns idempotently and rolls back only the hidden canary metadata", async () => {
useLocalEnvironment();
const t = convexTest(schema, modules);
const nativeSkillId = await seedNativeSkill(t, { exactSource: false, downloads: 91 });
await configureCanary(t);
const first = await runCanary(t);
const repeated = await runCanary(t);
expect(repeated.run.counts).toMatchObject({
observed: 1,
inserted: 0,
updated: 0,
unchanged: 1,
scansPlanned: 0,
routeCollisions: 1,
});
const rollback = await t.mutation(internal.skillsShCatalog.rollbackFixtureRunInternal, {
runId: repeated.runId,
actor: "codex-test",
reason: "remove only the controlled canary metadata",
confirm: "rollback-skills-sh-controlled-canary",
});
const native = await t.run(async (ctx) => await ctx.db.get(nativeSkillId));
const catalogEntries = await t.run(
async (ctx) => await ctx.db.query("skillsShCatalogEntries").collect(),
);
expect(rollback).toMatchObject({
fixtureId: "patrick-html-canary-v1",
deletedEntries: 1,
nativeSkillsChanged: 0,
});
expect(catalogEntries).toHaveLength(0);
expect(native).toMatchObject({
_id: nativeSkillId,
statsDownloads: 91,
stats: { downloads: 91 },
});
expect(first.runId).not.toBe(repeated.runId);
});
});
+1
View File
@@ -72,6 +72,7 @@
"skill-cards:worker": "bun scripts/skill-cards/run-skill-card-worker.ts",
"skills-sh:capture-500": "bun scripts/skills-sh-catalog/capture-frozen-snapshot.ts",
"skills-sh:prove-500": "bun scripts/skills-sh-catalog/prove-500.ts",
"skills-sh:prove-canary": "CLAWHUB_TEST_CATALOG_MODE=controlled-canary bun scripts/skills-sh-catalog/run-test-gate.ts",
"skills:install": "npx --yes skills@1.5.16 add openclaw/design-system --skill openclaw-design openclaw-brand openclaw-design-system openclaw-marketing-pages openclaw-design-audit --agent codex --copy --yes",
"test": "vitest run",
"test:e2e": "vitest run -c vitest.e2e.config.ts",
+12 -1
View File
@@ -8,11 +8,18 @@ function requireEnv(name: string) {
const targetUrl = requireEnv("CLAWHUB_TEST_CATALOG_GATE_URL");
const operatorAuthorization = requireEnv("CLAWHUB_TEST_OPERATOR_TOKEN");
const mode = process.env.CLAWHUB_TEST_CATALOG_MODE?.trim() || "live-500";
if (mode !== "live-500" && mode !== "controlled-canary") {
throw new Error("CLAWHUB_TEST_CATALOG_MODE must be live-500 or controlled-canary");
}
const allowlist = (process.env.CLAWHUB_TEST_CATALOG_ALLOWLIST ?? "")
.split(",")
.map((externalId) => externalId.trim().toLowerCase())
.filter(Boolean);
if (allowlist.length > 10) throw new Error("CLAWHUB_TEST_CATALOG_ALLOWLIST cannot exceed 10");
if (mode === "controlled-canary" && allowlist.length > 0) {
throw new Error("The controlled canary does not admit scans");
}
async function callGate(body: Record<string, unknown>) {
const response = await fetch(targetUrl, {
@@ -28,7 +35,11 @@ async function callGate(body: Record<string, unknown>) {
const request = {
allowlist,
reason: "CLAW-556 bounded permanent Test proof",
mode,
reason:
mode === "controlled-canary"
? "CLAW-557 controlled hidden metadata canary"
: "CLAW-556 bounded permanent Test proof",
};
const execution = await callGate(request);
if (!execution.response.ok) {
@@ -12,6 +12,7 @@ const MAX_BATCH_SIZE = 50;
type CatalogTestRequest = {
allowlist?: string[];
mode?: "live-500" | "controlled-canary";
reason?: string;
};
@@ -22,12 +23,14 @@ function parseCatalogTestRequest(value: unknown): CatalogTestRequest | null {
(body.allowlist !== undefined &&
(!Array.isArray(body.allowlist) ||
!body.allowlist.every((externalId) => typeof externalId === "string"))) ||
(body.mode !== undefined && body.mode !== "live-500" && body.mode !== "controlled-canary") ||
(body.reason !== undefined && typeof body.reason !== "string")
) {
return null;
}
return {
...(body.allowlist !== undefined ? { allowlist: body.allowlist as string[] } : {}),
...(body.mode !== undefined ? { mode: body.mode as "live-500" | "controlled-canary" } : {}),
...(body.reason !== undefined ? { reason: body.reason as string } : {}),
};
}
@@ -105,6 +108,95 @@ async function executeSnapshotRun(
return { runId, run };
}
async function executeControlledCanaryRun(authorization: string, reason: string) {
const start = await callConvexOperator(authorization, {
method: "POST",
body: {
operation: "start-canary",
reason,
},
});
const runId = start.runId;
if (typeof runId !== "string") throw new Error("Convex Test operator did not return a run id");
const run = await callConvexOperator(authorization, {
method: "POST",
body: {
operation: "process-fixture",
runId,
},
});
if (run.status !== "completed" || run.cursor !== 1) {
throw new Error("Convex Test controlled canary did not complete exactly one row");
}
const reconciliation = await callConvexOperator(authorization, {
method: "POST",
body: {
operation: "reconcile",
runId,
},
});
if (
reconciliation.reconciled !== true ||
!Array.isArray(reconciliation.mismatches) ||
reconciliation.mismatches.length !== 0 ||
!Array.isArray(reconciliation.entries) ||
reconciliation.entries.length !== 1
) {
throw new Error("Convex Test controlled canary reconciliation failed");
}
return {
runId,
sourceVerification: start.sourceVerification,
run,
reconciliation,
};
}
function assertControlledCanaryControl(control: Record<string, unknown>) {
const expected = {
mode: "fixture",
discoveryEnabled: true,
writesEnabled: true,
scanPlanningEnabled: true,
scanAdmissionEnabled: false,
publicVisibilityEnabled: false,
maxEntriesPerRun: 1,
maxEntriesPerBatch: 1,
maxWritesPerBatch: 2,
maxPlannedScans: 1,
maxScanAdmissionsPerBatch: 0,
maxScanAdmissionsPerRun: 0,
maxScanAdmissionsPerDay: 0,
maxCatalogQueued: 0,
maxCatalogInFlight: 0,
} as const;
for (const [key, value] of Object.entries(expected)) {
if (control[key] !== value) {
throw new Error(`Convex Test controlled canary requires ${key}=${String(value)}`);
}
}
}
function assertControlledCanaryIdenticalRerun(run: Record<string, unknown>) {
const counts = run.counts;
if (!counts || typeof counts !== "object" || Array.isArray(counts)) {
throw new Error("Convex Test controlled canary identical rerun lacked counters");
}
const expected = {
observed: 1,
inserted: 0,
updated: 0,
unchanged: 1,
scansPlanned: 0,
scansAdmitted: 0,
} as const;
for (const [key, value] of Object.entries(expected)) {
if ((counts as Record<string, unknown>)[key] !== value) {
throw new Error("Convex Test controlled canary identical rerun was not idempotent");
}
}
}
function batchSizeFromControl(control: Record<string, unknown>) {
const maxEntriesPerBatch = control.maxEntriesPerBatch;
const maxWritesPerBatch = control.maxWritesPerBatch;
@@ -142,6 +234,10 @@ export default defineEventHandler(async (event) => {
if (allowlist.length > policy.maxRealScanAdmissions) {
return jsonResponse({ error: "allowlist_exceeds_test_ceiling" }, 400);
}
const mode = body.mode ?? "live-500";
if (mode === "controlled-canary" && allowlist.length > 0) {
return jsonResponse({ error: "controlled_canary_does_not_admit_scans" }, 400);
}
const memoryStart = process.memoryUsage();
const startedAt = Date.now();
try {
@@ -150,6 +246,44 @@ export default defineEventHandler(async (event) => {
if (!control || typeof control !== "object") {
throw new Error("Convex Test operator did not return catalog controls");
}
if (mode === "controlled-canary") {
assertControlledCanaryControl(control as Record<string, unknown>);
const reason = body.reason?.trim() || "CLAW-557 controlled hidden metadata canary";
const firstRun = await executeControlledCanaryRun(authorization, reason);
const identicalRerun = await executeControlledCanaryRun(
authorization,
`${reason} identical rerun`,
);
assertControlledCanaryIdenticalRerun(identicalRerun.run);
const memoryEnd = process.memoryUsage();
return jsonResponse({
ok: true,
mode,
source: firstRun.sourceVerification,
convex: {
deploymentName: staging.deploymentName,
buildSha: staging.buildSha,
firstRun,
identicalRerun,
},
runtime: {
elapsedMs: Date.now() - startedAt,
rssStartBytes: memoryStart.rss,
rssEndBytes: memoryEnd.rss,
heapUsedStartBytes: memoryStart.heapUsed,
heapUsedEndBytes: memoryEnd.heapUsed,
},
controls: {
publicVisibilityEnabled: false,
installabilityEnabled: false,
claimExecutionEnabled: false,
publisherAttachmentEnabled: false,
schedulesEnabled: false,
scanAdmissionEnabled: false,
maxEntriesPerRun: 1,
},
});
}
const batchSize = batchSizeFromControl(control as Record<string, unknown>);
const snapshot = await captureSkillsShCatalogTestSnapshot({
env: process.env,
@@ -205,6 +339,7 @@ export default defineEventHandler(async (event) => {
const memoryEnd = process.memoryUsage();
return jsonResponse({
ok: true,
mode,
source: {
project: "openclaw-foundation/clawhub",
vercelSourceSha: process.env.VERCEL_GIT_COMMIT_SHA ?? null,
+163
View File
@@ -23,6 +23,99 @@ vi.mock("./skillsShCatalogSource", () => ({
getSkillsShCatalogTestSourcePolicy: (...args: unknown[]) => sourcePolicyMock(...args),
}));
function controlledCanaryFetch(
identicalCounts: Record<string, number> = {
observed: 1,
inserted: 0,
updated: 0,
unchanged: 1,
scansPlanned: 0,
scansAdmitted: 0,
},
) {
return vi.fn(async (_url: string, init: RequestInit) => {
if (init.method === "GET") {
return new Response(
JSON.stringify({
deploymentName: "academic-chihuahua-392",
buildSha: "canary-sha",
control: {
mode: "fixture",
discoveryEnabled: true,
writesEnabled: true,
scanPlanningEnabled: true,
scanAdmissionEnabled: false,
publicVisibilityEnabled: false,
maxEntriesPerRun: 1,
maxEntriesPerBatch: 1,
maxWritesPerBatch: 2,
maxPlannedScans: 1,
maxScanAdmissionsPerBatch: 0,
maxScanAdmissionsPerRun: 0,
maxScanAdmissionsPerDay: 0,
maxCatalogQueued: 0,
maxCatalogInFlight: 0,
},
}),
);
}
const body = JSON.parse(String(init.body)) as {
operation: string;
runId?: string;
reason?: string;
};
if (body.operation === "start-canary") {
return new Response(
JSON.stringify({
runId: `skillsShCatalogRuns:${body.reason?.includes("identical") ? "repeat" : "first"}`,
sourceVerification: {
externalId: "patrick-erichsen/skills/html",
githubOwnerId: 20_157_849,
githubCommit: "050daba89f6b6636470add5cb300aac46a412cf8",
githubContentHash: "a47adb2c1ac33c088f664b5187971b63d2b958a7b9f01516d26005ca941a108f",
githubFetches: 4,
},
}),
);
}
if (body.operation === "process-fixture") {
const firstRun = body.runId?.endsWith("first");
return new Response(
JSON.stringify({
status: "completed",
cursor: 1,
counts: firstRun
? {
observed: 1,
inserted: 1,
updated: 0,
unchanged: 0,
scansPlanned: 1,
scansAdmitted: 0,
}
: identicalCounts,
}),
);
}
if (body.operation === "reconcile") {
return new Response(
JSON.stringify({
reconciled: true,
mismatches: [],
entries: [
{
externalId: "patrick-erichsen/skills/html",
publicVisible: false,
resolution: { installable: false },
},
],
}),
);
}
return new Response(JSON.stringify({ error: "unsupported_operation" }), { status: 400 });
});
}
describe("skills.sh permanent Test operator route", () => {
beforeEach(() => {
getHeaderMock.mockReset();
@@ -204,6 +297,76 @@ describe("skills.sh permanent Test operator route", () => {
expect(fetch).not.toHaveBeenCalled();
});
it("applies and reconciles the one-row controlled canary without OIDC or scan admission", async () => {
readBodyMock.mockResolvedValue({
mode: "controlled-canary",
reason: "CLAW-557 hidden metadata canary",
});
vi.stubGlobal("fetch", controlledCanaryFetch());
const handler = (await import("./routes/ops/skills-sh/catalog-test.post")).default;
const response = (await handler({} as never)) as Response;
expect(response.status).toBe(200);
expect(await response.json()).toMatchObject({
ok: true,
mode: "controlled-canary",
source: {
externalId: "patrick-erichsen/skills/html",
githubOwnerId: 20_157_849,
githubFetches: 4,
},
convex: {
firstRun: {
run: { counts: { inserted: 1, scansAdmitted: 0 } },
reconciliation: { reconciled: true, mismatches: [] },
},
identicalRerun: {
run: { counts: { unchanged: 1, scansPlanned: 0, scansAdmitted: 0 } },
reconciliation: { reconciled: true, mismatches: [] },
},
},
controls: {
publicVisibilityEnabled: false,
installabilityEnabled: false,
claimExecutionEnabled: false,
publisherAttachmentEnabled: false,
schedulesEnabled: false,
scanAdmissionEnabled: false,
},
});
expect(getVercelOidcTokenMock).not.toHaveBeenCalled();
expect(captureSnapshotMock).not.toHaveBeenCalled();
expect(fetch).toHaveBeenCalledTimes(7);
});
it("fails closed when the controlled canary rerun is not idempotent", async () => {
readBodyMock.mockResolvedValue({
mode: "controlled-canary",
reason: "CLAW-557 hidden metadata canary",
});
vi.stubGlobal(
"fetch",
controlledCanaryFetch({
observed: 1,
inserted: 0,
updated: 1,
unchanged: 0,
scansPlanned: 1,
scansAdmitted: 0,
}),
);
const handler = (await import("./routes/ops/skills-sh/catalog-test.post")).default;
const response = (await handler({} as never)) as Response;
expect(response.status).toBe(502);
expect(await response.json()).toEqual({
error: "skills_sh_catalog_test_failed",
message: "Convex Test controlled canary identical rerun was not idempotent",
});
});
it.each([
{ body: [], label: "array body" },
{ body: { allowlist: {} }, label: "non-array allowlist" },
+22
View File
@@ -50,6 +50,28 @@ skills:
This table is not an install artifact store. OpenClaw must not install from
`githubSkillContents`.
## Dark skills.sh discovery metadata
The skills.sh discovery pipeline has a separate hidden metadata table and does
not create or mutate `skills` rows during its planning gates.
- A discovery row keeps the immutable GitHub owner ID plus exact repository,
path, commit, folder content hash, source URL, and source snapshot.
- Native reconciliation is advisory metadata only. It classifies an observation
as new, an exact native GitHub-source match, or a slug/route collision.
- Exact matches may record the native download count for readback, but discovery
must not patch that count or any other native field.
- A verified GitHub owner may create a claim opportunity marker. That marker is
not publisher attachment, ownership, profile content, or claim execution.
- Hidden discovery rows are never installable and cannot become public through
the discovery control plane.
- The permanent Test canary uses one committed skills.sh observation fixture,
while GitHub owner, repository, commit, path, and content verification remain
live and authenticated.
- Canary rollback may delete only the controlled hidden fixture row. It must
leave native skills, download history, scan jobs, publishers, and aliases
unchanged.
`skills` stores the public catalog row and install state:
- `installKind: "github"`