@@ -162,20 +162,25 @@ def get_file_picker_prompt(
162162 - contributors grouped under the organization they belong to
163163 - explicit domain/email-pattern rules that the file defines for assigning
164164 contributors to organizations
165+
166+ What decides a match is the content, not the file's name or purpose. Any
167+ file qualifies when it states an organization per person — including
168+ governance or ownership files (e.g. OWNERS, MAINTAINERS) when they carry
169+ an explicit organization/employer for each person. Try to capture these.
165170 </what_to_find>
166171
167172 <what_to_reject>
168- Reject candidates whose preview does not explicitly associate contributors
169- with organizations , including:
170- - Lists of names, emails, or usernames with no stated organization
171- (e.g. AUTHORS, CONTRIBUTORS, CREDITS).
172- - Identity or alias mappings such as .mailmap.
173- - Governance or ownership files that name people but not their employer
174- (e.g. OWNERS, CODEOWNERS, MAINTAINERS without organization information).
175- - Source code, scripts, or configuration files.
176-
177- Email addresses and email domains alone do not make a file a match , unless
178- the file explicitly defines those domains or patterns as affiliation rules .
173+ The deciding factor is whether the file states an organization per person.
174+ Reject a candidate when it only identifies people without that , including:
175+ - lists of names, emails, or usernames with no organization
176+ (e.g. AUTHORS, CONTRIBUTORS, CREDITS)
177+ - identity or alias mappings (e.g. .mailmap)
178+ - role or ownership files that name people but not their employer
179+ (e.g. OWNERS, CODEOWNERS, MAINTAINERS without organization information)
180+ - source code, scripts, or configuration
181+
182+ An email address or its domain is not an organization , unless the file
183+ explicitly defines that domain or pattern as an affiliation rule .
179184 </what_to_reject>
180185
181186 <candidates>
@@ -298,9 +303,16 @@ def get_extraction_prompt(self, content_to_analyze: str) -> str:
298303 It is valid to use an email/domain pattern only when the file itself explicitly
299304 defines that pattern as an affiliation rule.
300305 - name: the organization name the file states, else null.
301- - domain: use a domain the file states; otherwise infer it from the stated
302- organization name only when confident (e.g. "Google" -> google.com), else null.
303- Never infer a domain from an email.
306+ - domain: choose the organization's domain in this order:
307+ 1. a domain the file explicitly states for the organization;
308+ 2. a domain you can infer confidently from the stated organization name
309+ (e.g. "Google" -> google.com);
310+ 3. only when the file explicitly ties an organization to this contributor
311+ AND provides that same contributor's email, the domain of that email
312+ (e.g. company "Ericsson Software Technology" + "john@est.tech" -> est.tech).
313+ Otherwise null. An email domain is a domain source only for an organization
314+ the file has already named for that person — never use it to invent or guess
315+ an organization that the file does not state.
304316 - isUnaffiliated: set true only when the file explicitly marks the person as
305317 independent / unaffiliated / personal / no employer — not as a fallback when
306318 the organization is merely missing. When true, set name and domain to null.
@@ -409,7 +421,7 @@ def group_parse_rows(
409421 if is_unaffiliated :
410422 stint = AffiliationOrganizationStint (
411423 name = "Individual" ,
412- domain = "individual-noaccount .com" ,
424+ domain = "nonameaccount .com" ,
413425 date_start = cls ._parse_optional_date (organization .date_start ),
414426 date_end = cls ._parse_optional_date (organization .date_end ),
415427 is_unaffiliated = True ,
@@ -607,13 +619,35 @@ def has_existing_stint(
607619
608620 @staticmethod
609621 def affiliation_stint_key (
610- contributor : AffiliationContributor , domain : str
611- ) -> tuple [str , str , str ] | None :
612- domain = domain .lower ()
622+ contributor : AffiliationContributor ,
623+ organization : AffiliationOrganizationStint ,
624+ ) -> tuple [str , str , str , date | None , date | None , bool ] | None :
625+ domain = organization .domain .lower ()
626+ date_start = organization .date_start
627+ date_end = organization .date_end
628+ if isinstance (date_start , datetime ):
629+ date_start = date_start .date ()
630+ if isinstance (date_end , datetime ):
631+ date_end = date_end .date ()
632+
613633 if contributor .email :
614- return ("email" , contributor .email .lower (), domain )
634+ return (
635+ "email" ,
636+ contributor .email .lower (),
637+ domain ,
638+ date_start ,
639+ date_end ,
640+ organization .is_unaffiliated ,
641+ )
615642 if contributor .github :
616- return ("github" , contributor .github .lower (), domain )
643+ return (
644+ "github" ,
645+ contributor .github .lower (),
646+ domain ,
647+ date_start ,
648+ date_end ,
649+ organization .is_unaffiliated ,
650+ )
617651 return None
618652
619653 async def exclude_parent_repo_affiliations (
@@ -633,16 +667,15 @@ async def exclude_parent_repo_affiliations(
633667 key
634668 for entry in parent_snapshot
635669 for organization in entry .organizations
636- if (key := self .affiliation_stint_key (entry .contributor , organization . domain ))
670+ if (key := self .affiliation_stint_key (entry .contributor , organization ))
637671 }
638672
639673 fork_entries : list [AffiliationContributorEntry ] = []
640674 for entry in extracted_affiliations :
641675 organizations = [
642676 organization
643677 for organization in entry .organizations
644- if (key := self .affiliation_stint_key (entry .contributor , organization .domain ))
645- is None
678+ if (key := self .affiliation_stint_key (entry .contributor , organization )) is None
646679 or key not in parent_stint_keys
647680 ]
648681 if organizations :
@@ -720,6 +753,7 @@ async def apply_affiliations(
720753 organization_identity_inputs .append (
721754 {
722755 "type" : "primary-domain" ,
756+ "platform" : "email" ,
723757 "value" : organization .domain ,
724758 "verified" : True ,
725759 }
0 commit comments