From 2e57ab71db73b498c286720aa2257a9b42d78dd3 Mon Sep 17 00:00:00 2001 From: tryeverything24 Date: Mon, 20 Jul 2026 04:53:10 -0700 Subject: [PATCH] fix(content-lane): add snake_case aliases to protectedFrontmatterFields (#7445) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit protectedFrontmatterFields listed every protected field in camelCase only, so an entry that wrote a protected field in the legitimately-accepted snake_case convention (e.g. download_url instead of downloadUrl) was invisible to protectedFrontmatterChanges — the gate compared before["downloadUrl"]/after["downloadUrl"], both undefined, saw no change, and the protected-close never fired even though the underlying value changed. Same divergence class as #7250 (urlFields vs sourceUrlFields), already fixed in this repo via PR #7269. This adds the 19 snake_case aliases for every multi-word camelCase member (author, category, disclosure, slug are single all-lowercase words and don't need one). Data-only addition to the Set; protectedFrontmatterChanges's comparison logic is untouched. Closes #7445 --- .../review/content-lane/content-repo-spec.ts | 24 +++++++++++++++++++ test/unit/content-lane-duplicates.test.ts | 18 ++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/packages/loopover-engine/src/review/content-lane/content-repo-spec.ts b/packages/loopover-engine/src/review/content-lane/content-repo-spec.ts index 3775fda10b..a2998f7d96 100644 --- a/packages/loopover-engine/src/review/content-lane/content-repo-spec.ts +++ b/packages/loopover-engine/src/review/content-lane/content-repo-spec.ts @@ -66,6 +66,30 @@ export const AWESOME_CLAUDE_CONTENT_SPEC: ContentRepoSpec = { "submittedByUrl", "sourceSubmissionNumber", "sourceSubmissionUrl", + // snake_case aliases, matching urlFields/sourceUrlFields's pairing convention (#7445, same divergence class as + // #7250): protectedFrontmatterChanges compares before[field]/after[field] by the literal parsed key, so a + // protected field written in the legitimately-accepted snake_case convention (e.g. `download_url`) was invisible + // to this gate — a real protected-close bypass. `author`, `category`, `disclosure`, `slug` are single + // all-lowercase words (camelCase and snake_case are byte-identical), so no separate alias is needed for them. + "affiliate_url", + "author_profile_url", + "claim_status", + "claim_url", + "date_added", + "download_url", + "import_pr_number", + "import_pr_url", + "package_url", + "package_verified", + "pricing_model", + "reviewed_at", + "reviewed_by", + "reviewed_pr_number", + "submitted_at", + "submitted_by", + "submitted_by_url", + "source_submission_number", + "source_submission_url", ]), urlFields: new Set([ "documentationUrl", diff --git a/test/unit/content-lane-duplicates.test.ts b/test/unit/content-lane-duplicates.test.ts index 49b83c73e9..9d63f1846b 100644 --- a/test/unit/content-lane-duplicates.test.ts +++ b/test/unit/content-lane-duplicates.test.ts @@ -96,6 +96,24 @@ describe("protectedFrontmatterChanges", () => { const after = mdx({ title: "T", slug: "a", author: "Alice" }); expect(protectedFrontmatterChanges(before, after)).toEqual([]); }); + + it("flags a changed protected field written in the snake_case alias (e.g. download_url), matching the camelCase behavior (#7445)", () => { + // Before #7445, protectedFrontmatterFields listed only downloadUrl (camelCase), so an entry using the + // legitimately-accepted snake_case key was invisible to this gate even though urlFields already treats + // download_url as an equally-valid alias for duplicate detection. + const before = mdx({ title: "T", slug: "a", download_url: "https://example.com/old.zip" }); + const after = mdx({ title: "T", slug: "a", download_url: "https://example.com/new.zip" }); + expect(protectedFrontmatterChanges(before, after)).toEqual(["download_url"]); + }); + + it("every multi-word camelCase protectedFrontmatterFields member has its snake_case alias present too (#7445, mirrors #7250's urlFields pairing assertion)", () => { + const singleLowercaseWords = new Set(["author", "category", "disclosure", "slug"]); + const toSnakeCase = (field: string): string => field.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`); + for (const field of AWESOME_CLAUDE_CONTENT_SPEC.protectedFrontmatterFields) { + if (singleLowercaseWords.has(field) || field.includes("_")) continue; + expect(AWESOME_CLAUDE_CONTENT_SPEC.protectedFrontmatterFields.has(toSnakeCase(field))).toBe(true); + } + }); }); describe("extractContentDuplicateSignals + strict match", () => {