PyPI · Process pypi/support PEP 541 PyPI Onboarding · No. 11 7,226 words · ~36 min Onboarding

Support Field Guide

Seventy commits, 11,770 issues. The repository is not a codebase. It is the written procedure for every decision a PyPI administrator has to make about a person.

The queue

A repository that is a queue

pypi/support holds ten files. Nine of them are Markdown or YAML. The tenth is a GitHub Actions workflow added in May 2026, and it is the first executable thing the repository has ever contained.

Against those ten files sit 11,770 issues: 961 open, 10,809 closed. The repository has 70 commits. That works out to roughly 168 issues for every commit, and the ratio is the point. Nothing here ships. The files exist so that a volunteer reading issue number 11,000 makes the same decision a volunteer made on issue number 300.

The PyPI Universe field guide introduced this repository as one of two in the organization that ship no code, and quoted the line that draws the boundary. Here it is again, because everything downstream follows from it:

“Any issue which relates to an individual user or project, or requires administrator or moderator intervention, should be opened here. … Any issue that affects how PyPI works for all users, or require code or configuration changes, should not be opened here.”

pypi/support → README.md

The README then does four more things the summary skipped, and each one is a routing rule.

It refuses to enumerate its own scope. Rather than list every accepted request type, it says “See this page for the exact list” and points at the template chooser. The forms are the specification. When a form is added or removed, the README needs no edit, and in practice gets none.

It sends bugs, feature requests and usability complaints to pypi/warehouse. That link was corrected in May 2024 to say pypi rather than pypa. Two other pointers in the repository were not corrected and still name the old organization, which is worth knowing before you trust a URL you find here.

It refuses to be the security channel. Vulnerability reports go to pypi.org/security/, and the README declines to restate the policy on the grounds that the hosted version is the current one. A public issue tracker is a poor place to disclose a vulnerability, and a stale copy of a security policy is a poor place to read one.

It gives you an escape hatch. Under “If you are unsure where to file your issue” the README tells you to open one anyway and it will be transferred as necessary. The link there still reads github.com/pypa/pypi-support, an old name GitHub redirects. The hatch matters more than the stale URL: a queue that punishes people for guessing wrong gets fewer correct filings, not more.

Everything else is triage vocabulary. The repository carries 21 labels, and most name a decision rather than a component: PEP 541, name squat, mass name squat, similar project name, prohibited project name, quarantine, limit request, account-recovery, unverified email, typo email, awaiting response, requires triaging. The list tells you what kinds of trouble arrive here.

Intake

Six ways in, and one way out

Six issue forms live in .github/ISSUE_TEMPLATE/. They were plain Markdown templates until February 2021, when they were converted to GitHub issue forms: structured YAML with typed fields, required validations and enforced checkboxes. That conversion is the single most consequential change in the repository's history. A Markdown template is a suggestion. A form with validations: required: true will not let the issue be opened.

FileForm nameLabel applied
access-issues.ymlNetwork Access Issuesnetwork
account-recovery.ymlAccount Recoveryaccount-recovery
limit-request-file.ymlSize Limit Request (per-file)limit request
limit-request-project.ymlSize Limit Request (project-wide)limit request
mass-name-squatter.ymlMass name squatting by usermass name squat
pep541-request.ymlName retention related request (PEP 541)PEP 541

Each form applies exactly one label. That is not decoration. The workflow in section seven depends on it, and so does every saved search a volunteer uses to work the queue.

The network form asks for a packet capture in prose

access-issues.yml is the longest file in the repository and reads like a diagnostic script written for someone who has never run one. It opens with two admonitions: check status.python.org first, including past incidents, and understand that this form covers pypi.org, test.pypi.org and files.pythonhosted.org only, not python.org or docs.python.org. Those two paragraphs deflect a large share of what would otherwise arrive.

Then it asks for evidence, field by field, with the commands pre-typed inside fenced code blocks so the reporter only has to paste output. Required: platform details, dig for A and AAAA records on both hostnames, IPv4 traceroutes, IPv4 curl -vvv against a JSON endpoint and against one specific pinned file, and IPv4 openssl s_client. Optional: every IPv6 equivalent, on the grounds that not everyone has it.

One field is optional for a different reason. The fastly-debug.com blob is the most useful single artifact for diagnosing a CDN problem, and the form says so, then immediately warns that “this output is optional, as it encodes identifying information about your browser, public IP, and location.” It also warns that ad blockers will break the page. A form that tells you what it is about to collect, and lets you decline, is doing something most support intake does not.

The recovery form sets expectations before it takes information

account-recovery.yml is short: username, reason, and two mandatory checkboxes. The first makes you assert that you never generated or have lost the eight recovery codes that PyPI issues when two-factor authentication is enabled. That single checkbox closes the largest category of recovery request, because the honest answer is often that the codes exist in a password manager.

The second is labelled Acknowledgement, and its description is the most candid sentence in the repository: “By their nature account recovery issues are labor intensive. The PyPI team has limited resources available for their completion. There may be a delay of a week or more between responses for these issues.” You cannot open the issue without ticking “I understand that it may take a significant amount of time to process my account recovery request.”

The limit forms need the project to exist first

Both size-limit forms carry a required checkbox reading “Does this project already exist?” The per-file form is blunt about it: “This project MUST EXIST for an increased limit to be set. An empty 0.0.0 release may be uploaded, if the project doesn't exist. You can delete this release immediately after uploading it.”

The requirement looks backwards until you remember where the limit lives. A file-size limit is a column on a project row in Warehouse's database. There is no place to record a limit for a project that has not been created, so the administrator cannot grant the request. The instruction to upload an empty 0.0.0 and then delete it is the workaround for a data model, published as user-facing policy.

Both forms also carry a checkbox that only exists because GitHub cannot do it for you: “I have updated the title.” The templates ship with placeholder titles (File Limit Request: PROJECT_NAME - 000 MiB and Project Limit Request: PROJECT_NAME - 00 GiB) and the queue is worked from the issue list, so an unedited title is a ticket nobody can triage without opening. Four of the six forms ask the reporter to rewrite the title by hand: the two limit forms enforce it with this checkbox, and mass-name-squatter.yml and pep541-request.yml ask for the same edit in prose.

There is a comment in both files worth stealing. The checkbox label is written as "Yes" in quotes, with the note: # should be a str but YAML parses unwrapped yes/no as bool. Someone hit that and left a sign.

The PEP 541 form is an evidence-collection sheet

The name-retention form does not ask what you want. It asks for the material a moderator will need in order to apply PEP 541, and its fields line up with the PEP's criteria one for one. Project URL and your PyPI username, both as required inputs. Reasons, phrased carefully as “Do you believe the project has been abandoned.” A dropdown that forces the request into one of two categories, Maintenance or Replacement, because the PEP applies different tests to each. Source repository URLs for both the current project and the one you intend to publish in its place, which is how you demonstrate the improvements the PEP requires. And a required field for contact attempts and research, with a clause that quietly limits what your evidence buys you: “Note moderators are obliged to contact owners independently.”

Your failed contact attempts are a precondition, not a substitute for the process. The moderators run their own.

config.yml: the door policy

One line in config.yml does most of the work: blank_issues_enabled: false. There is no way to open an untyped issue. Every arrival is one of six shapes, carries one label, and has already answered the questions the answer depends on.

The rest of the file is a routing table of six contact links, shown above the template list and leading away from the repository:

LinkGoes to
Python Infrastructure Status Pagestatus.python.org, before reporting an outage
Organizations Feature Supportdocs.pypi.org/organization-accounts/support/#organization-features
Organizations Billing Support…/#organization-billing, for billing and type changes
Feature Request, UX issue or bug?Warehouse's issue chooser
Security Policypypi.org/security/
PSF Code of Conductpypa/.github → CODE_OF_CONDUCT.md

A seventh form used to exist. organizations.yml was added in May 2023 and deleted on 2 April 2025 by a commit titled “redirect orgs to support inbox.” Organization support moved off the public tracker and onto a private inbox, which is the correct call for a channel that carries billing details. The same day brought a second commit, “mailto links not allowed,” which is why the two Organizations entries point at documentation pages rather than at an address. GitHub will not accept a mailto: URL in a contact link, so the replacement route goes through docs.

Policy one

Upload limits, and the lists that decide them

PyPI ships two default ceilings: 100 MiB for a single file, and 10 GiB for a project's total footprint. Both are stated in the descriptions of the two limit forms. Raising either is a human decision, and the README is the only written record of how that decision is made.

It opens with the position, not the procedure: “Generally, projects should try to minimize the size of their distributions as much as possible. Large distributions consume significant amounts of PyPI's resources, and also generally do not provide a good user experience.” Then three lists, quoted here in full because a volunteer quotes them back verbatim.

Small increases, up to 200 MiB, are generally granted when:

Large increases, more than 200 MiB, are generally granted when:

Increases are generally denied when:

Read the lists against each other and the shape becomes clear. The same artifact appears on both sides, separated only by size: a small pre-trained model is granted, a large one is denied; a small JAR is granted, a large one is denied. The word doing the work is the adjective. 200 MiB is the hinge, and above it the burden shifts from “what is in the file” to “who are you,” which is why the only two large-grant reasons are hardware coverage and an established project or organization behind the name.

Two denial criteria are about behaviour rather than content. Frequent releases are denied because the cost is the product of size and cadence, not size alone: a 300 MiB nightly is 9 GB a month of storage and mirror traffic. Shipping another language's runtime is denied because the request is really a request to use PyPI as a general artifact store.

Now look back at the forms and the mapping is exact. Every criterion in the README has a field that feeds it.

Form fieldCriterion it answers
About the project: “what the project is and how long it has been active”Associated with an established project or organization
“If you have made efforts to reduce the size… mention it here”Has not made a best-effort to reduce release size
“If you bundle other packages… if you bundle example data…”Includes a runtime, a JAR, or bundled binaries
How large is each release? (project-wide form)Which threshold applies, small or large
How frequently do you make a release? (project-wide form)Makes nightly releases and/or is published frequently
Which indexes: PyPI, TestPyPI, or bothWhere to apply the change, since the two indexes hold separate limits

The project-wide form even supplies a model answer in its placeholder text, showing the level of detail expected: “~200 MiB total. 2 Linux wheels at ~60 MiB, due to bundled libxml binaries. 6 Windows/MacOS wheels at ~10 MiB. The source tarball at ~10 MiB.” The form is teaching the requester how to make a case that can be granted.

Checkpoint 1 · the queue and its forms

Q1A maintainer wants a higher per-file limit for a project name that has never been uploaded. What does the form require first?

B. The checkbox is mandatory and the description is emphatic: “This project MUST EXIST for an increased limit to be set.” A limit is a value stored against an existing project in Warehouse, so there is nowhere to write it until the project row exists. The empty-release workaround is published policy, not folklore.

Q2A project ships a 250 MiB wheel per platform because it bundles CUDA kernels. Which README criterion is it argued under?

A. Above 200 MiB the small-increase list stops applying, and GPU support is named explicitly in the large-increase list. D is the trap worth understanding: compiled kernels are not a pre-trained model, and the two are treated differently on purpose. Weights are data that could live elsewhere; the binaries are the package.

Q3Why does config.yml set blank_issues_enabled: false?

D. The forms are the specification of what this repository accepts, which is why the README delegates its own scope list to the template chooser. One label per form is load-bearing: it drives triage searches and, since May 2026, the autoresponder that counts the queue ahead of you. A blank issue would carry no label and match no saved search.

Policy two

PEP 541, the law it is running

PEP 541, “Package Index Name Retention,” was written by Łukasz Langa in January 2017 and resolved in March 2018. It is a Process PEP, and it went through an approval path no other packaging PEP uses: the BDFL-Delegate recommended it to the PSF's Packaging Working Group, which voted on it after consulting the PSF's General Counsel. Amendments take the same route. The PEP says why in one sentence: the policy has potential legal ramifications for the Python Software Foundation.

Its premise is that a flat namespace makes a name a finite resource, and that as the index ages, collisions between the current use of a name and a proposed use become constant. Everything else is a set of tests.

Reachability

The PEP puts the burden on the owner: “The user of the Package Index is solely responsible for being reachable by the Package Index maintainers for matters concerning projects that the user owns.” Maintainers must try at least three times, through three channels: the email on the user's PyPI profile, the address in the project's Author field, and any address found in the project's documentation on the index or on its listed home page. Then the sentence that starts every clock in the runbook: “The maintainers stop trying to reach the user after six weeks.”

Abandoned, and everything else

A project is abandoned when all three hold: the owner is not reachable, there have been no releases in the past twelve months, and there is no activity from the owner on the project's home page (or no home page is listed). One line disposes of every other project on the index: “All other projects are considered active.”

Abandonment alone changes nothing. “Projects are never removed from the Package Index solely on the basis of abandonment. Artifacts uploaded to the Package Index hold inherent historical value.” What abandonment does is unlock two named procedures, with different bars.

To take over an abandoned project and keep maintaining it, all five must hold: the project is abandoned by the test above; the candidate demonstrates their own failed attempts to contact the owner; the candidate demonstrates improvements made on their own fork; the candidate demonstrates why a fork under a different name is not an acceptable workaround; and the index maintainers have no additional reservations. Then a sentence that ends most disputes on its own: “Under no circumstances will a name be reassigned against the wishes of a reachable owner.”

To take the name for a different project, the bar rises. The same five conditions apply, with “improvements on a fork” replaced by proof that the replacement project already exists and meets notability requirements, plus one more that no amount of arguing can supply: “download statistics on the Package Index for the existing package indicate project is not being used.”

Active projects: the refusal

The PEP declines the hardest cases outright. “The maintainers of the Package Index are not arbiters in disputes around active projects.” It then lists three real-world patterns and says none of them qualify: co-maintainers who part ways and both want the name; someone who owned the name off-index and found it taken on-index, “even if User A's project X gains notability and the User B's project X is not notable”; and a contributor whose fixes go unreleased, “even if User A agrees to publish a new version and later doesn't.” The recommendation is to get in touch and solve it by respectful communication.

That refusal is the load-bearing part of the document. It means the index is not a trademark registry and will not be used as one.

Invalid projects and infringement

A separate list covers removal on sight: non-conformance with the Terms of Use, malware, spam, illegal content, violation of copyright, trademarks, patents or licenses, Code of Conduct violations, obfuscation used to hide functionality, and abuse of the index for purposes it was not intended for. Among them sits the definition that the runbook uses constantly: “project is name squatting (package has no functionality or is empty).”

Intellectual property is routed away from the queue entirely. The PSF and the index maintainers do not pre-screen uploads; possibly infringing packages go to legal@python.org, and counsel decides. A package may be removed or transferred “at the sole discretion of the Python Software Foundation to address a claim of infringement,” and in some cases before the owner responds.

Who decides

Index maintainers escalate to the Packaging Workgroup when a matter is not clear enough. Code of Conduct violations and legal claims require judgement by the PSF Board, whose recommendations then go back to the Workgroup for review. The final sentence names the authority without hedging: “The Packaging Workgroup has the final say in any disputes covered by this document and can decide to reassign or remove a project from the Package Index after careful consideration even when not all requirements listed here are met.”

Note the last clause. The written criteria bind the process, not the outcome.

The runbook

Where the law gets a clock

PEP 541 tells you what must be true. It does not tell you what to do on a Tuesday with an issue in front of you. name-retention/README.md, added in April 2024, does. It is 215 lines: a Mermaid flowchart, four email templates, and seven canned GitHub comments.

It begins by naming its own limits, in two sentences placed above the diagram rather than buried at the end:

“This is incomplete and doesn't account for special cases. From anywhere in the flow, if the process feels wrong: move to Special case and consult with colleagues or PyPI admins.”

pypi/support → name-retention/README.md

The flowchart has a node called Special case with no incoming edges drawn, because every node is an incoming edge. A procedure that ships with a documented exit is a procedure people will follow, because it does not ask them to pretend it covers everything.

New PEP 541 ticket Already transferred or deleted? yes comment, then TBD no Project has uploads? no such project TBD no uploads yes Any functionality? none some What is the request? replacement “why not use a different name?” continued maintenance INVALID PROJECT · NAME SQUATTING Send courtesy notice Wait about one week Owner responded? yes → TBD no Send removal notice Comment: name available TRANSFER OF AN ABANDONED PROJECT Comment: initial response + disclaimer Send reachability mail to four addresses Wait two weeks Third mail sent? no, repeat yes Send transfer notice, recommend transfer owner replies at any point: keep the project, or hand it over. both are recommendations, not decisions. three mails, two weeks apart, is the six-week limit PEP 541 sets on contact attempts. PyPI Administrators final determination
Fig. 1. The name-retention flow, redrawn from the Mermaid source in name-retention/README.md. Dashed edges are branches that leave the main path. Every terminal box is a recommendation; the decision belongs to the administrators. From any node, a volunteer may exit to Special case.

The four questions

The flow triages before it investigates, and each question is cheap to answer from the project page.

  1. 01

    Is it already resolved?

    NEW_isdone

    Requests sit long enough that the situation moves without them. If the transfer or deletion already happened, comment and stop.

  2. 02

    Does the project have uploads?

    NEW_uploads

    No uploads means an empty name, which PEP 541 defines as squatting. That routes straight to the removal lane and never touches the abandonment test. A third answer exists, “no such project,” because people file against names that were never registered.

  3. 03

    Does it have functionality?

    NEW_funct

    Uploads that do nothing are the other half of the PEP's squatting definition, “has no functionality or is empty.” Same lane. The asterisk in the source acknowledges this is a judgement call.

  4. 04

    Maintenance, or replacement?

    NEW_what

    A working project that someone wants to continue enters the transfer lane. A working project that someone wants to replace gets a comment, not a lane. The canned reply quotes the PEP's own line about historical value and asks the question the criteria imply: why can you not use a different name?

The squatting lane, in one week

Courtesy notice, wait, removal notice. The courtesy notice states the finding and offers the owner a way out: “If you would like to release a project under this name, please do so as soon as possible. Otherwise, we will recommend that the PyPI Administrators delete it in about a week.” If nothing changes, the removal notice goes out and a comment records that the name will become available.

The transfer lane, in six weeks

The reachability email is the centre of the whole document. It goes to four addresses at once: $PYPI_USER_ADDRESS, $PROJECT_AUTHOR_ADDRESS, $UPLOADER_ADDRESS and $ADDRESS_FROM_DOCS. Those are PEP 541's three channels, expanded to four because the uploader and the account owner are not always the same person. The subject line is ACTION REQUIRED: PyPI $PROJECT name retention.

The body states the request, links the support issue, and then does something worth copying: it offers the owner the cheapest possible way to end the matter on their own terms.

If you would like to keep the project, please reply to this email.

If you are willing to give up the project, it would help us if you do it
yourself: go to https://pypi.org/manage/project/$PROJECT/collaboration/,
log in, and invite $CANDIDATE as an Owner.

Three attempts, two weeks apart, with one paragraph of difference between them. The first adds nothing. The second says “This is our second attempt to reach you. We tried two weeks ago.” The third is the one that matters:

This is our third and final attempt to reach you. If you do not reply in
two weeks, we will recommend that the PyPI Administrators transfer
ownership of PROJECT to CANDIDATE.

Three mails at two-week intervals plus a final two-week wait is the six weeks PEP 541 allows, spent evenly. All four templates close the same way: “Thank you for your time, PyPI Support.”

The templates use two notations. $VARIABLE marks a substitution. Braces with a pipe mark a choice the volunteer makes: {transferring ownership to them in order to continue maintaining it|to reuse the name for another project}, or {is empty|has no functionality}. One template covers both request types because the only difference is a clause.

The disclaimer

Every initial GitHub comment carries the same paragraph, and it is the sentence that keeps the whole arrangement honest:

“Disclaimer: We are providing support to the PyPI Administrators to validate this request and make a recommendation on the outcome and actions to be taken. Final determination will be made by the PyPI Administrators when our process is complete.”

name-retention/README.md → Initial Response

Read the seven comment templates and four of them end in the words “we recommend”: the invalid-project removal, the reachable-owner close, the owner-requested transfer, and the abandoned-project transfer. Every action-oriented outcome is framed as a recommendation, never a decision. The other three don't need the word: the two intake templates end on the same final-determination disclaimer instead, and the different-name template ends by asking the requester a question. Volunteers gather evidence, run the clock and write a finding. They do not transfer anything. The one comment that closes a case rather than escalating it is “Owner is reachable,” which restates the PEP's hardest rule in two sentences: “$OWNER responded to our email, indicating that they want to keep the project. Projects are not transferred against the wishes of a reachable owner.”

The delta

What the runbook adds to the PEP

PEP 541 is a policy document approved by a working group after legal review, so it is written to survive cases nobody has thought of yet. That makes it vague in exactly the places a volunteer needs a number. The runbook supplies the numbers without contradicting a line of it.

QuestionPEP 541The runbook
How many contact attempts? “at least three times” Exactly three, each a separate template with escalating language
How long between attempts? Unspecified; “stop after six weeks” Two weeks, three times, then a final two-week wait
Contact where? Three channels, described Four $ variables in one To: line, sent together
What does the mail say? Nothing Subject line, body, three escalation paragraphs, common closing
Squatting: how long to fix it? Nothing. Squatting is simply “invalid” Courtesy notice, then “about a week”
Reachable owner says no? “Under no circumstances… against the wishes of a reachable owner” A canned comment recommending the issue be closed
Replacement request on a working project? Five criteria including notability and download statistics One comment quoting the PEP and asking why a different name will not do
Who decides? Packaging Workgroup has final say; maintainers escalate PyPI Administrators, named in a disclaimer on every first reply
What if the process is wrong? Workgroup may act “even when not all requirements… are met” Exit to Special case from any node, consult colleagues or admins

The last row is the pair to study. Both documents build the same escape, from opposite ends. The PEP grants the Workgroup power to act outside the written criteria. The runbook grants the volunteer permission to stop following it. Neither pretends the rules are complete.

One row is a genuine gap rather than a refinement. PEP 541 names the Packaging Workgroup as the final authority; the runbook names the PyPI Administrators. Those are not the same body, and neither document explains the relationship between them: no source here establishes an appeal procedure, or says which body defers to which. A reader who has only the runbook will not learn how that's resolved, and neither will this guide pretend to know.

Checkpoint 2 · the PEP and the runbook

Q4A project's last release was three years ago. The owner replies to the first reachability email and says they want to keep the name. What happens?

C. Abandonment requires all three conditions, and the first is that the owner is not reachable. A reply ends the test. The PEP says it directly: “Under no circumstances will a name be reassigned against the wishes of a reachable owner.” The runbook has a template for exactly this, and its recommendation is to close. The remaining attempts are moot; their only purpose was to establish unreachability.

Q5A well-maintained project holds a name someone else wants for a more popular library. No abandonment. What does PEP 541 provide?

A. “The maintainers of the Package Index are not arbiters in disputes around active projects,” and the PEP lists this exact scenario as non-qualifying, adding that it holds “even if User A's project X gains notability and the User B's project X is not notable.” Notability appears in the PEP only as an extra requirement on the candidate in an abandonment case, never as a way to beat an active project. B is a real path but a different one: it needs an actual trademark, and it goes to counsel rather than to the queue.

Q6Where do the runbook's two-week intervals come from?

B. The PEP gives two numbers, “at least three times” and “stop trying to reach the user after six weeks,” and no interval. The runbook derives one: three mails two weeks apart, with the third promising a decision in another two weeks. That is the whole relationship between the documents in one arithmetic step.

The exception

One file that runs

The “no code” description of this repository was accurate until 7 May 2026, when a commit titled “feat: support autoresponder” added .github/workflows/response-time-comment.yml. Two follow-up commits on 13 and 15 May tightened its filtering and collapsed its API calls. It is 137 lines, and it is the only executable file pypi/support has.

It fires on issues: [opened], skips issues opened by bots or by anyone whose author_association is OWNER, MEMBER or COLLABORATOR, reads the issue's single label, counts how many open issues with that label are waiting ahead of it, and posts one comment.

The count is the interesting part. An issue is not counted as ahead of you if it is already on a project board, or if any non-bot member has commented on it. What remains is the number of issues in your category that nobody has touched. The query runs as one paginated GraphQL search that pulls project items and the first hundred comments for each candidate in the same request, replacing what the first version did in two REST calls per issue.

Then it says this, to every person who opens an issue:

“PyPI support is one paid staffer, plus volunteers, plus whatever part-time hours other PSF staff have to give. Often that's zero. If you'd like to help fund more staffing, the PSF takes donations at python.org/psf/donations. Either way, someone will get to your request eventually.”

.github/workflows/response-time-comment.yml

Every policy document above becomes legible once you read that sentence. The forms are long because the reviewer's time is the scarce resource. The runbook exists because the reviewer will not be the same person twice. The disclaimer exists because volunteers are not administrators. The queue design is a staffing constraint written down.

Why the workflow is worth reading as an example

A workflow triggered by issues: opened in a public repository handles attacker-controlled input by definition. Anyone can open an issue, and the title and body are theirs to write. This file is careful in ways that are cheap to copy.

The comments are the unusual part. Most hardened workflows are hardened without saying why, which means the next editor removes the hardening by accident. This one explains each decision at the point where someone would undo it.

Checkpoint 3 · the autoresponder

Q7The workflow counts issues “ahead of” a new one. Which open, same-label, older issues does it exclude?

D. Both exclusions mean the same thing: someone has picked the issue up. A project item is a triaged issue; a member comment is a worked one. What is left is genuinely untouched, which is the number that predicts your wait. A is close but wrong in a way that matters, since a reply from another user is not work done.

Q8Why does the workflow read the issue body and labels through JavaScript rather than ${{ github.event.issue.body }} in a run: step?

C. GitHub expands ${{ }} textually into the script it is about to execute, so an issue title containing shell metacharacters runs with the job's token. Reading the same value as a JS property is inert. The file states the rule as a comment: the only interpolated expression in it is github.event.issue.number, an integer GitHub generates.

The map

Where everything is

ThingWhereWhy you'd look
What belongs in the queue README.md The should and should-not lists, plus the pointer to Warehouse for bugs
The exact accepted request types the template chooser The forms are the specification; the README delegates to this page
Upload-limit criteria README.md → “Guidelines for upload limit requests” Three lists and one 200 MiB hinge. The only written policy
Default limits .github/ISSUE_TEMPLATE/limit-request-*.yml 100 MiB per file, 10 GiB per project, stated in the form descriptions
Name-retention law PEP 541 Abandonment test, transfer criteria, the refusal to arbitrate, final authority
Name-retention procedure name-retention/README.md Flowchart, four email templates, seven canned comments, all the timers
Where requests are routed away .github/ISSUE_TEMPLATE/config.yml Six contact links: status page, Organizations, Warehouse, security, CoC
Security reports pypi.org/security/ Never the issue tracker. The repository refuses to hold a copy
Infringement claims legal@python.org PEP 541 routes these to PSF counsel, not to moderators
The autoresponder .github/workflows/response-time-comment.yml Queue-depth logic, and a workflow-security example worth copying
Where this repository sits The PyPI Universe field guide The two process repositories, and the ten that hold the code

If you're going to work on this

Five things learned from reading the queue rather than the code.

  1. Read the six forms before the README. They define the scope; the README only summarizes it, and the README is edited less often than the forms are.
  2. Answer with the lists, not with your opinion. The upload-limit criteria and the PEP 541 criteria exist so that two volunteers reach the same result, and so that a refusal can be shown to be a policy rather than a mood.
  3. Never write a sentence that sounds like a decision. Every action-oriented comment in the runbook says “we recommend,” and the disclaimer says who decides. That distinction is what lets volunteers work name disputes at all.
  4. Check whether the owner replied before checking anything else. Reachability collapses the abandonment test, and half the investigation stops being relevant.
  5. If a case does not fit, take the exit the runbook gives you. “If the process feels wrong: move to Special case and consult with colleagues or PyPI admins.” Forcing an odd case through the flowchart produces a defensible-looking wrong answer, which is worse than an open ticket.

Pyramid terms, plain English

ACL / __acl__
A list of (allow or deny, who, permission) rules a resource can define. Pyramid's authorization layer checks it before letting a request through.
config.scan()
Walks the codebase looking for decorators like @view_config and registers whatever it finds. Without a scan, a decorator alone does nothing.
directive
A custom method bolted onto Pyramid's Configurator object (via add_directive), so a plugin can offer its own verb, like config.add_periodic_task(...), instead of every caller repeating the same boilerplate.
find_service()
Asks the registry for "whatever implements this interface" and gets an object back, without the caller knowing or caring which concrete class it is.
includeme(config)
The one function every Pyramid plugin must define. config.include("some.package") calls it and hands over the Configurator, so the plugin can register its own routes, views, and services.
predicate
A condition attached to a route or view, like request_method="POST", that Pyramid checks before it will match that route or view to an incoming request.
register_service_factory()
The other half of find_service(): tells the registry "when someone asks for this interface, call this factory to build the answer."
registry
Pyramid's central lookup table, assembled once at startup, holding every route, view, service, and setting the running app knows about.
resource
In traversal, an object reachable by walking a chain of __getitem__ calls from the root. The traversal equivalent of a URL path segment.
root factory
The function that builds the very first resource in a traversal chain, given a request. Every other resource is reached by indexing into it.
subscriber
A function registered to run automatically whenever a specific event fires, like a transaction commit, without the code that fires the event knowing the subscriber exists.
traversal
Pyramid's alternative to matching a URL against a pattern: walk a resource tree one path segment at a time via __getitem__, and whatever object you land on becomes the view's context.
tween
Short for "between." Middleware that sits between Pyramid's router and the view, in an explicit, orderable chain, wrapping the whole request/response cycle.
venusian
The library config.scan() actually uses. A decorator like @view_config just leaves a note for venusian to find later; it does nothing by itself until scanned.
view deriver
A wrapper Pyramid can apply to every view during registration, used for cross-cutting behavior (like injecting a metrics call) without every view author remembering to add it by hand.