Context
packages/loopover-engine/src/review/content-lane/content-repo-spec.ts defines
protectedFrontmatterFields (line 45) — the set of frontmatter keys whose change on a MODIFIED
entry triggers a hard "protected close" via protectedFrontmatterChanges
(src/review/content-lane/duplicates.ts:154). Every entry in this set is written in camelCase only
(downloadUrl, packageUrl, authorProfileUrl, claimUrl, pricingModel, reviewedAt,
reviewedBy, reviewedPrNumber, submittedAt, submittedBy, submittedByUrl,
sourceSubmissionNumber, sourceSubmissionUrl, claimStatus, dateAdded, importPrNumber,
importPrUrl, packageVerified, affiliateUrl).
parseSimpleFrontmatter (src/review/content-lane/duplicates.ts:59) keys parsed frontmatter by
the literal YAML key text with no case normalization. The sibling list urlFields
(content-repo-spec.ts:70) proves snake_case is a genuinely accepted convention for at least two
of these same field names (downloadUrl/download_url, packageUrl/package_url are both
explicitly paired there already). Because protectedFrontmatterFields has no snake_case aliases,
an entry that writes any of these protected fields in snake_case (e.g. download_url: instead of
downloadUrl:) is invisible to protectedFrontmatterChanges — the gate compares
before["downloadUrl"] vs after["downloadUrl"], both undefined, sees no change, and the
protected-close never fires, even when the underlying value was actually changed.
This is the same divergence class issue #7250 already fixed for sourceUrlFields vs urlFields
(closed 2026-07-19, PR #7269) — same file, same root cause (two field-name lists meant to describe
one concept, only one kept in sync with the camelCase/snake_case pairing convention).
Concrete failure scenario: a "modified entry" PR changes an existing entry's download_url:
(or claim_url:, pricing_model:, author_profile_url:, etc. — any snake_case-keyed protected
field) to a different, attacker-controlled value. The identical edit written as downloadUrl:
would be caught by protectedFrontmatterChanges and hard-close the PR as a protected-field
modification; written as download_url:, the gate reports zero protected-field changes and the PR
proceeds through the normal (non-protected) review path — a real gate bypass for supply-chain/
monetization/identity fields, the exact class of edit this gate exists to catch.
Requirements
protectedFrontmatterFields must include the snake_case alias for every multi-word camelCase
member, following the exact pairing convention urlFields already uses: 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. (author, category,
disclosure, and slug are single, all-lowercase words — camelCase and snake_case are
byte-identical for these, so no separate alias is needed or should be added for them.)
- Do not change
protectedFrontmatterChanges's comparison logic itself, duplicates.ts's other
consumers, or any other field list in content-repo-spec.ts — this is a data-only addition to
the one constant.
- The fix must land in
packages/loopover-engine/src/review/content-lane/content-repo-spec.ts
(the real implementation) — src/review/content-lane/content-repo-spec.ts is a
export * re-export shim (see its own header comment) and picks up the change automatically; do
not duplicate the constant there.
Deliverables
Test Coverage Requirements
This repo's Codecov patch gate is 99%+ hard (branch-counted) on every changed line/branch in
src/**/packages/**. Both new regression tests above must actually exercise
protectedFrontmatterChanges end-to-end (real before/after frontmatter strings through
parseSimpleFrontmatter), not just assert on the raw Set contents.
Expected Outcome
A content entry that writes any protected field in the accepted snake_case convention is now
protected by the same close gate as its camelCase-keyed equivalent — no more silent bypass of the
identity/provenance/monetization protected-edit gate based purely on which naming convention a
submission happens to use.
Links & Resources
packages/loopover-engine/src/review/content-lane/content-repo-spec.ts:45 (protectedFrontmatterFields,
the incomplete list), :70 (urlFields, the complete camelCase+snake_case pairing to mirror).
src/review/content-lane/duplicates.ts:59 (parseSimpleFrontmatter, no case normalization),
:154 (protectedFrontmatterChanges, the gate this feeds). Issue #7250 / PR #7269 — the identical
fix already applied to sourceUrlFields.
Context
packages/loopover-engine/src/review/content-lane/content-repo-spec.tsdefinesprotectedFrontmatterFields(line 45) — the set of frontmatter keys whose change on a MODIFIEDentry triggers a hard "protected close" via
protectedFrontmatterChanges(
src/review/content-lane/duplicates.ts:154). Every entry in this set is written in camelCase only(
downloadUrl,packageUrl,authorProfileUrl,claimUrl,pricingModel,reviewedAt,reviewedBy,reviewedPrNumber,submittedAt,submittedBy,submittedByUrl,sourceSubmissionNumber,sourceSubmissionUrl,claimStatus,dateAdded,importPrNumber,importPrUrl,packageVerified,affiliateUrl).parseSimpleFrontmatter(src/review/content-lane/duplicates.ts:59) keys parsed frontmatter bythe literal YAML key text with no case normalization. The sibling list
urlFields(
content-repo-spec.ts:70) proves snake_case is a genuinely accepted convention for at least twoof these same field names (
downloadUrl/download_url,packageUrl/package_urlare bothexplicitly paired there already). Because
protectedFrontmatterFieldshas no snake_case aliases,an entry that writes any of these protected fields in snake_case (e.g.
download_url:instead ofdownloadUrl:) is invisible toprotectedFrontmatterChanges— the gate comparesbefore["downloadUrl"]vsafter["downloadUrl"], bothundefined, sees no change, and theprotected-close never fires, even when the underlying value was actually changed.
This is the same divergence class issue #7250 already fixed for
sourceUrlFieldsvsurlFields(closed 2026-07-19, PR #7269) — same file, same root cause (two field-name lists meant to describe
one concept, only one kept in sync with the camelCase/snake_case pairing convention).
Concrete failure scenario: a "modified entry" PR changes an existing entry's
download_url:(or
claim_url:,pricing_model:,author_profile_url:, etc. — any snake_case-keyed protectedfield) to a different, attacker-controlled value. The identical edit written as
downloadUrl:would be caught by
protectedFrontmatterChangesand hard-close the PR as a protected-fieldmodification; written as
download_url:, the gate reports zero protected-field changes and the PRproceeds through the normal (non-protected) review path — a real gate bypass for supply-chain/
monetization/identity fields, the exact class of edit this gate exists to catch.
Requirements
protectedFrontmatterFieldsmust include the snake_case alias for every multi-word camelCasemember, following the exact pairing convention
urlFieldsalready uses: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. (author,category,disclosure, andslugare single, all-lowercase words — camelCase and snake_case arebyte-identical for these, so no separate alias is needed or should be added for them.)
protectedFrontmatterChanges's comparison logic itself,duplicates.ts's otherconsumers, or any other field list in
content-repo-spec.ts— this is a data-only addition tothe one constant.
packages/loopover-engine/src/review/content-lane/content-repo-spec.ts(the real implementation) —
src/review/content-lane/content-repo-spec.tsis aexport *re-export shim (see its own header comment) and picks up the change automatically; donot duplicate the constant there.
Deliverables
protectedFrontmatterFieldsinpackages/loopover-engine/src/review/content-lane/content-repo-spec.tsextended with the 19 snake_case aliases listed abovesnake_case-keyed protected field (e.g.
download_url:from one URL to another, all otherfrontmatter unchanged) is now reported by
protectedFrontmatterChangesas a protected-fieldchange, matching what the equivalent camelCase-keyed (
downloadUrl:) edit already producestoday
protectedFrontmatterFieldsthat isn't a single all-lowercase word has its snake_case counterpart also present in the set
(mirroring the pairing-assertion test content-repo-spec's sourceUrlFields omits the snake_case aliases its sibling urlFields already has #7250's fix added for
sourceUrlFields/urlFields)Test Coverage Requirements
This repo's Codecov patch gate is 99%+ hard (branch-counted) on every changed line/branch in
src/**/packages/**. Both new regression tests above must actually exerciseprotectedFrontmatterChangesend-to-end (real before/after frontmatter strings throughparseSimpleFrontmatter), not just assert on the rawSetcontents.Expected Outcome
A content entry that writes any protected field in the accepted snake_case convention is now
protected by the same close gate as its camelCase-keyed equivalent — no more silent bypass of the
identity/provenance/monetization protected-edit gate based purely on which naming convention a
submission happens to use.
Links & Resources
packages/loopover-engine/src/review/content-lane/content-repo-spec.ts:45(protectedFrontmatterFields,the incomplete list),
:70(urlFields, the complete camelCase+snake_case pairing to mirror).src/review/content-lane/duplicates.ts:59(parseSimpleFrontmatter, no case normalization),:154(protectedFrontmatterChanges, the gate this feeds). Issue #7250 / PR #7269 — the identicalfix already applied to
sourceUrlFields.