Skip to content

Commit 527d978

Browse files
fix(content-lane): recognize snake_case distribution and primary source fields
Mirror #7250's urlFields pairing so download_url/package_url and github_url/repo_url/repository_url/source_url get the same source-evidence treatment as their camelCase aliases. Closes #7446 Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 4ae3854 commit 527d978

2 files changed

Lines changed: 43 additions & 2 deletions

File tree

packages/loopover-engine/src/review/content-lane/content-repo-spec.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,10 @@ export const AWESOME_CLAUDE_CONTENT_SPEC: ContentRepoSpec = {
143143
"website_url",
144144
],
145145
sourceUrlListFields: new Set(["sourceUrls", "retrievalSources"]),
146-
distributionSourceFields: new Set(["downloadUrl", "packageUrl"]),
146+
// snake_case aliases match urlFields / sourceUrlFields (#7446): without them, a site-relative
147+
// `download_url`/`package_url` is misclassified as canonical (spurious invalid_url), and a
148+
// retryable `github_url`/`repo_url`/`repository_url`/`source_url` is silently downgradable.
149+
distributionSourceFields: new Set(["downloadUrl", "packageUrl", "download_url", "package_url"]),
147150
distributionSourceHosts: new Set([
148151
"crates.io",
149152
"files.pythonhosted.org",
@@ -160,5 +163,14 @@ export const AWESOME_CLAUDE_CONTENT_SPEC: ContentRepoSpec = {
160163
"rubygems.org",
161164
"www.npmjs.com",
162165
]),
163-
primaryCanonicalSourceFields: new Set(["githubUrl", "repoUrl", "repositoryUrl", "sourceUrl"]),
166+
primaryCanonicalSourceFields: new Set([
167+
"githubUrl",
168+
"repoUrl",
169+
"repositoryUrl",
170+
"sourceUrl",
171+
"github_url",
172+
"repo_url",
173+
"repository_url",
174+
"source_url",
175+
]),
164176
};

test/unit/content-lane-source-evidence.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,16 @@ describe("extractSubmittedSourceUrls", () => {
5252
expect(urls).toHaveLength(0);
5353
});
5454

55+
it("drops a site-relative snake_case download_url the same way as downloadUrl (#7446)", () => {
56+
// Before #7446, distributionSourceFields was camelCase-only, so download_url:/… stayed in the
57+
// extracted set, was classified as canonical, and produced a spurious invalid_url hard failure.
58+
expect(extractSubmittedSourceUrls(mdx({ download_url: "/downloads/skills/foo.zip" }))).toHaveLength(0);
59+
expect(extractSubmittedSourceUrls(mdx({ package_url: "/packages/foo.tgz" }))).toHaveLength(0);
60+
for (const field of ["download_url", "package_url"]) {
61+
expect(AWESOME_CLAUDE_CONTENT_SPEC.distributionSourceFields.has(field)).toBe(true);
62+
}
63+
});
64+
5565
it("reads snake_case source fields (e.g. the canonical source_url), matching urlFields (#7250)", () => {
5666
// Before #7250, sourceUrlFields listed only the camelCase names, so a legitimately-aliased snake_case field
5767
// was invisible to the source-evidence gate even though duplicates.ts (which reads urlFields) saw it.
@@ -816,6 +826,25 @@ describe("downgrade rules (hasVerifiableCanonicalSource / isDowngradableInconclu
816826
expect(primary?.blocking).toBe(true);
817827
expect(report.status).toBe("retryable");
818828
});
829+
830+
it("does NOT downgrade a snake_case PRIMARY-field retryable either (#7446)", async () => {
831+
// Before #7446, primaryCanonicalSourceFields was camelCase-only, so a retryable source_url was
832+
// treated as non-primary and silently downgraded whenever another canonical was reachable.
833+
const src = mdx({
834+
githubUrl: "https://github.com/acme/anchor",
835+
source_url: "https://flaky.example/primary-snake",
836+
});
837+
const report = await checkSubmittedSourceEvidence(
838+
src,
839+
fakeFetch({ "https://github.com/acme/anchor": 200, "https://flaky.example/primary-snake": 503 }),
840+
);
841+
const primary = report.urls.find((u) => u.field === "source_url");
842+
expect(primary?.blocking).toBe(true);
843+
expect(report.status).toBe("retryable");
844+
for (const field of ["github_url", "repo_url", "repository_url", "source_url"]) {
845+
expect(AWESOME_CLAUDE_CONTENT_SPEC.primaryCanonicalSourceFields.has(field)).toBe(true);
846+
}
847+
});
819848
});
820849

821850
// ── summary / decision string branches ───────────────────────────────────────────────────────────

0 commit comments

Comments
 (0)