CI: classify PR files as core by component ownership - #15444
Merged
patmmccann merged 3 commits intoAug 5, 2026
Conversation
Extract the "is this file core?" logic out of getPRProperties.js into coreFiles.js, and replace the heuristic it used. The old rule treated everything as core except test/example files, modules matching an adapter naming convention, and libraries referenced by a single vendor. That flagged as core the autogenerated per-module metadata, the vendor-specific libraries shared by an adapter and its aliases, and the registries every new module has to touch. Core is now what no outside component owns: a module is core when its metadata declares no component or only `prebid` ones, and a library is core when a core module pulls it in (prebid-core included). Modules with no metadata yet - newly added ones - fall back to the naming conventions, and to whether their name starts with a registered component name. Over the 112 PRs opened or updated in the last two weeks this flags 25 as core, down from 38, with none newly flagged. Repo-wide it flags 260 files and 38 modules, down from 1063 files. coreFiles.js also runs as a CLI, to classify an arbitrary list of files: gh pr diff --name-only 1234 | node .github/workflows/scripts/coreFiles.js Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Attributing a library through the chunk graph alone misses prebid-maintained shared code whose consumers all happen to be vendor modules: ortbConverter (135 consumers), dnt (47), boundingClientRect (37), sizeUtils (33) and about thirty others were not flagged. Counting consumers, as the previous heuristic did, does not separate those from white-label libraries shared between one vendor's brands - teqblazeUtils has 55 consumers, more than pbsExtensions or devicePixelRatio. Neither does counting distinct component owners or gvl ids, nor the mix of component types among the consumers. So libraries now default to core, and are excluded when they belong to a vendor: when their name begins with a registered component name, or when they are listed in VENDOR_LIBRARIES - white labels whose brand is not itself a component, or whose components are registered under a longer name than the library. A library that a core module pulls in stays core whatever its name suggests, which is what keeps timeoutQueue - it reads as an extension of the `timeout` rtd component - on the right side. This keeps all 21 libraries that were core before and adds 35, while dropping the 47 vendor-owned ones. Repo-wide the classification covers 318 files; the 112 PRs from the last two weeks are unaffected, still 25 flagged core. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0a34542eaf
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Barecheck - Code coverage reportTotal: 96.67%Your code coverage diff: 0.00% ▴ ✅ All code changes are covered |
A library added together with the adapter that uses it was flagged core: PR-assignment.yml runs from master, where the new vendor is not yet in metadata/modules.json, so no name test can recognize the library as theirs. Of the new-adapter-with-its-own-library PRs merged since the component registry exists, five would have been mislabelled this way - prebid#13731, prebid#13815, prebid#14267, prebid#14807, prebid#14896 - and four of them are not flagged core today. A library with exactly one consumer now belongs to that consumer, whatever its name suggests; it becomes core as soon as a second module picks it up. Libraries a core module uses are still core, so this only applies to code owned outright by one vendor module. Counting consumers beyond that is still avoided - a threshold cannot separate shared infrastructure from a white label, since teqblazeUtils has more consumers than pbsExtensions - so ortbConverter, dnt and the other shared libraries stay core. Four existing libraries move with this: cryptoUtils, htmlEscape, interpretResponseUtils and vastTrackers, each a single vendor's helper with a generic name. Core libraries go from 56 to 52, repo-wide from 318 files to 314; the last two weeks of PRs are unchanged at 25 flagged core. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
patmmccann
approved these changes
Aug 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Type of change
Description of change
PR-assignment.ymllabels a PRcoreand requires a second Prebid reviewer when any changed file looks like a core file. The rule that decided this lived inline ingetPRProperties.js: everything counts as core except test/example/top-level/workflow files, modules matching an adapter naming convention, and libraries referenced by a single vendor.That produces a lot of false positives. It flags the autogenerated per-module metadata under
metadata/modules/, the vendor-specificlibraries/*Utilsshared between an adapter and its aliases, and the registries (metadata/modules.json,modules/.submodules.json) that every new module has to touch.This PR moves the logic into
.github/workflows/scripts/coreFiles.jsand replaces the heuristic with one based on component ownership:prebidcomponents. Modules with no metadata at all — a newly added module, since metadata is generated separately from the module it describes — fall back to the naming conventions:<vendor>BidAdapter,AnalyticsAdapter,RtdProvider,IdSystem,VideoProvider, or a name that begins with a component name already registered inmetadata/modules.json(adlooxAdServerVideo→adloox). The prefix has to end on a camelCase boundary, so a component namedcurrencywould not makemodules/currency.jslook vendor-owned.dependencies.json—prebid-corefalls out of this naturally, since its metadata declares onlyprebidcomponents. Failing that, a library with a single consumer is that consumer's own code; otherwise it is core unless it belongs to a vendor. Libraries default to core: most of them are shared code that happens to have only vendor modules as consumers (ortbConverter,dnt,sizeUtils), and a library extracted tomorrow should be reviewed until someone says otherwise. Vendor libraries are recognized by name, the same way metadata-less modules are.metadata/modules.json,metadata/disclosures/modules/, andmodules/.submodules.jsonare excluded: a change there is about the module being registered, not about the file itself.Two constants hold what no convention can pick out.
VENDOR_MODULEShas one entry,seenthisBrandStories: vendor-owned, but with no metadata, no conventional name, and no component registered under its vendor's name.VENDOR_LIBRARIEShas eleven, mostly white labels whose own brand is not a component (teqblazeUtils,vizionikUtils) or whose components are registered under a longer name than the library (intentIqUtils→intentIqId,advangUtils→advangelists). Both lists shrink as the modules involved gain metadata.coreFiles.jsalso runs as a CLI, so the classification can be checked against any set of files:Effect on recent PRs
Measured against the 112 PRs opened or updated in the two weeks to 2026-08-04, using each PR's file list from the GitHub API and a locally built
dependencies.json.No PR is newly flagged as core. 16 stop being flagged:
libraries/appnexusUtils/anUtils.jsmetadata/modules.json,metadata/modules/copper6sspBidAdapter.jsonlibraries/vidazooUtils/bidderUtils.jslibraries/blueUtils/bidderUtils.jslibraries/nexx360Utils/index.ts(+3)modules/.submodules.jsonmetadata/modules/adbixBidAdapter.jsonmetadata/modules.json,metadata/modules/goplBidAdapter.json(+1)libraries/vidazooUtils/bidderUtils.jsmetadata/disclosures/modules/stackupRtdProvider.jsonmodules/.submodules.jsonlibraries/precisoUtils/bidUtils.js,libraries/precisoUtils/bidUtilsCommon.jslibraries/agenticxUtils/bidderUtils.jsmetadata/modules.json,metadata/modules/bidfabrikBidAdapter.jsonlibraries/mgidUtils/mgidSessionStorage.tslibraries/vidazooUtils/bidderUtils.js,libraries/vidazooUtils/vidazooTypes.tsThe 25 that remain core are the
src/changes, the build/metadata tooling (metadata/compileMetadata.mjs,customize/), the prebid-owned modules (userId,rtdModule,consentManagement*,topicsFpdModule,adChoices,devtoolsMcp,gamAdServerVideo) and the libraries they use.What is flagged core repo-wide
314 of 3772 tracked files, down from 1076. Modules go from 44 to 38; libraries from 76 to 52, but not the same 52 — the ones dropped are vendor-owned, and shared code that no core module happens to use is now kept.
Modules (38 of 753)
Declaring only
prebidcomponents (4):debugging,topicsFpdModule,userId,validationFpdModuleWith no metadata and no vendor name (34)
_moduleMetadata,adChoices,allowActivities,bidResponseFilter,bidViewability,bidViewabilityIO,consentManagementGpp,consentManagementTcf,consentManagementUsp,currency,dataControllerModule,dchain,dsaControl,enrichmentLiftMeasurement,fpdModule,gamAdServerVideo,gppControl_usnat,gppControl_usstates,gptPreAuction,idImportLibrary,instreamTracking,multibid,nativeRendering,previousAuctionInfo,priceFloors,rtdModule,rules,s2sTesting,schain,sizeMapping,sizeMappingV2,storageControl,tcfControl,videoModuleLibraries (52 of 103)
Consumer counts in parentheses. 21 of these are core because a core module pulls them in; the other 31 because they have several consumers and nothing marks them as a vendor's.
analyticsAdapter(70),autoplayDetection(4),bidViewabilityPixels(2),bidderTimeoutUtils(2),boundingClientRect(37),chunk(21),cmp(3),connectionInfo(18),consentManagement(4),cookieSync(2),currencyUtils(8),dealUtils(2),devicePixelRatio(10),dfpUtils(1),dnt(47),domainOverrideToRootDomain(4),fingerprinting(26),fpdUtils(7),gamUtils(0),getOrigin(3),gptUtils(29),greedy(0),keywords(14),metadata(714),mspa(2),navigatorData(4),objectGuard(1),ortb2.5StrictTranslator(0),ortb2.5Translator(5),ortb2Utils(31),ortbConverter(135),pbsExtensions(13),percentInView(17),placementPositionInfo(4),processResponse(2),purposeDeclarations(1),schainSerializer(2),sizeUtils(33),storageDisclosure(1),timeToFirstBytesUtils(2),timeoutQueue(5),timezone(14),transformParamsUtils(2),uid1Eids(2),urlUtils(20),userAgentUtils(9),userSyncUtils(7),video(4),viewport(1),weakStore(0),webdriver(3),xmlUtils(2)The 51 that are not core, all owned by a single vendor
adagioUtils,adkernelUtils,adrelevantisUtils,adtelligentUtils,advangUtils,agenticxUtils,alliance_gravityUtils,appnexusUtils,asteriobidUtils,audUtils,biddoInvamiaUtils,blueUtils,braveUtils,browsiUtils,cryptoUtils,deepintentUtils,dspxUtils,dxUtils,encypherUtils,equativUtils,ferioUtils,htmlEscape,hybridVoxUtils,hypelabUtils,intentIqConstants,intentIqUtils,interpretResponseUtils,liveIntentId,magniteUtils,mediaImpactUtils,medianetUtils,mgidUtils,nexverseUtils,nexx360Utils,omsUtils,pageInfosUtils,permutiveUtils,precisoUtils,pubmaticUtils,riseUtils,smartyadsUtils,targetVideoUtils,teqblazeUtils,uid2Eids,uid2IdSystemShared,uniquestUtils,utiqUtils,vastTrackers,vidazooUtils,vizionikUtils,xeUtilsBeyond the single-consumer case, the consumer count is deliberately not part of the rule. The previous heuristic counted consuming vendors, which flagged as core every white-label library shared between a vendor's brands;
teqblazeUtilshas 55 consumers, more thanpbsExtensions(13) ordevicePixelRatio(10), so no threshold separates shared infrastructure from a white label.Everything under
src/,creative/,plugins/,customize/and the build tooling stays core, as before — the rule only changes howmodules/andlibraries/are judged.Known limitations
Four libraries have no recorded user at all —
gamUtils,greedy,weakStoreandortb2.5StrictTranslator— so they fall into the "unknown owner" branch and stay core by assumption rather than by lookup.webpack.conf.jsgives every directory underlibraries/its ownsplitChunkscacheGroup, so a library gets a<lib>.jschunk — and therefore an entry independencies.json— whenever its code survives into a build. No chunk means the code is not in the bundle, and each of the four is absent for its own reason:gamUtilsdfpUtils; webpack elides the intermediate module, sogamAdServerVideorecords a dependency ondfpUtilsinsteadgamAdServerVideois core, so correct attribution would reach the same answergreedysrc/utils/promise.tsbehindFEATURES.GREEDY, whichgulpHelpers.jsdisables by default; tree-shaken out ofchunk-core.jssrc/is coreweakStoretest/ortb2.5StrictTranslatortest/All four end up core anyway, since none of them is vendor-named, so the blind spot costs nothing today. It would matter for a vendor library that fell out of the chunk graph the way
gamUtilsdoes.gamUtilsis worth removing on its own merits regardless — it is a one-line re-export with a single consumer, which could importdfpUtilsdirectly.PR-assignment.ymlchecks outmaster, so metadata lookups always reflectmasterwhiledependencies.jsoncomes from the PR build. A module added in the PR is therefore judged by name, which is what the naming fallback is for.Library classification leans on names, and names collide.
timeoutQueuebegins withtimeout, which is a registered rtd component, so the name test calls it vendor-owned; it stays core only becausecurrency,priceFloorsandrulesuse it. That ordering — usage first, name second — is deliberate, but a shared library with a colliding name and no core consumer would be missed. Converselyuid1Eidsis core whileuid2Eidsis not, purely becauseuid2is a registered component name anduid1is not, though the two are the same kind of file.A library extracted with only one consumer is treated as that consumer's, so genuinely shared code starts out non-core until a second module adopts it. This is what keeps a new adapter's own library from being flagged: on
masterthe vendor is not registered yet, so no name test can recognize it. Four existing libraries are classified this way —cryptoUtils(sevioBidAdapter),htmlEscape(adgenerationBidAdapter),interpretResponseUtils(craftBidAdapter) andvastTrackers(medianetAnalyticsAdapter) — each a single vendor's helper with a generic name.VENDOR_LIBRARIESis maintained by hand and will drift as white labels are added. It is the smaller of the two possible lists — white-label platforms appear rarely, while shared libraries are extracted constantly — and the failure mode of forgetting an entry is a PR flagged core that need not have been, not a core change slipping through.The general fix for both the
VENDOR_MODULESexception and the metadata-less modules is for those modules to declare a component, the waymetadata/core.jsonalready does fordebugging,topicsFpdModule,userIdandvalidationFpdModule. That would turn a 34-module guess into a lookup.Testing
.github/is excluded from eslint and there is no unit test harness for the workflow scripts, so this was verified by running the classifier over the repository and over the recent PR set, as reported above. The extraction step was checked separately: before the heuristic was changed, the extracted code agreed with the previous inline implementation on all 3771 tracked files.Other information
No documentation PR is needed; this does not change anything user-facing.
Issue template
Type of issue — CI / tooling improvement. No associated issue.
Description — the
corelabel and the two-reviewer requirement it triggers are applied to PRs that only touch vendor-owned files, because the file classification treats per-module metadata, single-vendor libraries and module registries as core.Steps to reproduce — open a PR that changes only
libraries/vidazooUtils/bidderUtils.js(e.g. #15335) or only adds a new adapter and its metadata (e.g. #15374); thecorelabel is applied and two Prebid reviewers are required.Expected results — a PR touching only files owned by one vendor is not flagged as core.
Actual results — it is flagged as core. Of the 112 PRs from the last two weeks, 41 are flagged; 16 of those touch nothing but vendor-owned files.
Platform details — GitHub Actions,
actions/github-script@v9, Node 20+.This PR was generated by Claude Code.