Maintainer Load and Port Lifecycle Policy
1. Why this exists
Nothing in the handbook currently stops a committer from taking on more ports than they can realistically maintain, and nothing forces a stale, unmaintained port to actually get dealt with instead of sitting in the tree indefinitely. Being listed as a port's maintainer is supposed to mean that bug reports get triaged, that security issues get addressed, that the port gets bumped when upstream moves. When that stops being true for a large share of someone's ports, the cost doesn't stay with that maintainer only. It lands on whoever else has to touch the tree. Committers running exp-runs for a compiler or crypto library transition, or doing multi-architecture QA, end up doing de facto maintenance on ports someone else's name is attached to, for free, on top of their own work. A maintainer list that doesn't reflect who's actually maintaining anything is worse than an honest "unmaintained" flag, because it hides the real state of the tree from everyone who has to plan around it.
2. Maintainer load threshold
A committer or maintainer becomes overloaded, and ineligible to add new ports or adopt orphaned ones, once all of the following are true:
- They maintain 100 or more ports,
- Their outdated ratio (per portscout/repology, calculated as in section 2b) is 10% or higher,
- That ratio has remained at or above 10% continuously for 30 days or more.
Overloaded is a status, not a moment-to-moment calculation. Once the conditions above are met the maintainer remains overloaded until the status is cleared, whether or not each individual condition still holds.
Clearing overloaded status. The status is cleared once the maintainer's outdated ratio falls below 8% and remains below 8% for 30 continuous days. Entry and exit are deliberately not symmetric. Entry requires all three conditions; exit depends on the ratio alone. Two consequences follow, and both are intended:
- Shedding ports below 100 does not clear the status on its own. Dropping ports is still encouraged, and it lowers the ratio if the ports dropped were outdated ones. But a maintainer cannot reset their status by transferring away a handful of current ports while the backlog stays where it is.
- A maintainer whose ratio sits between 8% and 10% keeps whichever status they already hold. They do not become overloaded at 9%, and they do not stop being overloaded at 9%. This is what stops the status flipping on every recalculation.
The 30-day sustained window exists because a snapshot percentage is a bad trigger on its own. A batch of unrelated upstream releases landing the same week, or a short detection lag, can spike a ratio briefly with no real maintainer neglect behind it. What indicates an actual problem is a ratio that doesn't come back down i.e. when backlog accumulating rather than clearing. A maintainer who spikes and clears within a couple of weeks never trips this but one who sits above threshold for a month or more does.
Anti-gaming clause: The thresholds are meant to filter noise, not to provide a renewable reset. If a maintainer's ratio shows a repeating pattern of dropping below 8% just long enough to clear the status, followed by new port additions, followed by drift back toward or above 10%, that pattern is itself grounds for portmgr to decline to clear the status, or to reinstate it, regardless of the instantaneous number at any given check. A maintainer who actually caught up stays caught up.
Likewise, maintainers should not try to game these rules by obscuring maintainership, in particular by moving ports to a third party (“sock puppet”) that serves little purpose but to be a vehicle through which the overloaded maintainer can exert maintainership over ports without being the registered maintainer, or through the use of multiple e-mail addresses.
Overloaded maintainers are however encouraged to drop ports they feel unable to maintain further, including by reverting their maintainership to ports@FreeBSD.org.
2a. Taking on additional ports as an overloaded maintainer
While a maintainer is overloaded, taking on additional ports (new or adopted) requires explicit approval from portmgr to avoid further overload. In the following cases, overloaded maintainers may do so with portmgr's approval:
- Dependencies required for (in particular to update) ports of the overloaded maintainer
- Components or variants (including older and newer versions) of a software the overloaded maintainer already maintains
- The result of a refactor of ports the overloaded maintainer already maintains (e.g. when a port is split into multiple ports or a collection of ports is restructured)
- Software the maintainer personally and intensively uses or develops (is the upstream of)
Portmgr may approve or deny maintainership at the team’s own discretion for any reason.
The overloaded maintainer should strongly consider finding a volunteer to take over ports instead of requesting approval for maintainership.
2b. Pinned versions for dependency-locked ports
Some ports are outdated not from neglect but because a newer upstream version would break a dependency chain the maintainer doesn't control alone (e.g., a library bump that several downstream ports haven't caught up to yet). Where this applies, the maintainer should explicitly pin the version and document the reason in the port's Makefile or in an accompanying note or comment, rather than silently sitting outdated. A documented, justified pin does not count against the ratio in section 2 but an undocumented one does, since the whole point is distinguishing "waiting on a real constraint" from "hasn't been touched."
Setting PORTSCOUT=ignore does not, by itself, remove a port from consideration under section 2. It only suppresses portscout's own out-of-date flag for that port. It is not equivalent to a documented pin under this section and must not be used as a substitute for one. A maintainer cannot make a port disappear from the ratio calculation simply by silencing the tool that measures it. The port still needs an actual documented justification to be excluded, and portmgr may audit PORTSCOUT=ignore usage across a maintainer's ports for exactly this reason.
Separately, when calculating a maintainer's outdated ratio for section 2, portmgr should use the higher of the portscout and repology figures for a given port, not whichever number is more favorable to the maintainer. The two tools use different detection heuristics and don't always agree. Taking the higher figure avoids a maintainer's compliance status depending on which single tool happens to have missed something the other one caught.
2c. portmgr intervention once a maintainer is over threshold
Section 2 stops a maintainer from adding to their own pile once they're over threshold, but it doesn't by itself shrink the pile that's already there. Without a bridge to actual remediation, being "over threshold" just means the backlog sits frozen in place rather than getting worked down, which doesn't solve the underlying problem.
Once a maintainer is overloaded, portmgr may:
- Contact the maintainer directly to establish whether they intend to and are able to work the backlog down themselves, on what timeline.
If the maintainer cannot realistically clear it alone (this is the common case for anyone significantly over threshold and that's usually why they're over threshold), portmgr should assign or call for volunteer committers to help work through the maintainer's backlog: updating what's updatable, and identifying what should be deprecated per sections 5 and 6.
- portmgr, or a volunteer explicitly acting under portmgr's blanket approval for this purpose, is authorized to deprecate or remove specific ports from the maintainer's list under the section 5/6 criteria, without needing the maintainer's sign-off on each individual port. The maintainer keeps the right to fix and keep any given port instead, provided they do so within the standard deprecation window; the blanket approval exists so the backlog can actually move without requiring a case-by-case negotiation on every port, which is what has failed to produce results before now.
This is the mechanism that makes the rest of this policy actually operational. A threshold with no assigned path to remediation just produces a frozen backlog and a maintainer who's stuck but not helped, which isn't the goal.
3. Bug and PR handling expectations
- A maintainer is expected to triage (not necessarily resolve) a new PR or bug report against their port within 30 days: acknowledge it, close it as invalid, forward it upstream with a note back on the PR, or fix it.
- "I filed it upstream" does not, by itself, close the loop. If upstream doesn't act within a reasonable window and the bug is real (build failure, security issue, wrong behavior), the maintainer is expected to either patch it locally or explicitly mark the port as needing help / accepting patches.
- Ports with more than 20 open PRs older than 90 days count against the maintainer's load for purposes of section 2, independent of the outdated-ratio calculation.
4. New port acceptance
Before a new port is accepted, the submitter/maintainer should be able to answer:
- Is upstream actively maintained (commits, releases, or a responsive issue tracker within the last 12 months)? If not, don't bring it in as a fresh port.
Is there a real, identifiable use case that a meaningful share of users would actually exercise as opposed to "it exists on GitHub and could theoretically be useful"? We're aiming to cover what the majority of users need, not every corner case anyone could conceivably want. A user with a genuinely niche need can request a port through Bugzilla; that's a better gate than a committer adding it on the strength of it merely existing.
- Does the software actually function meaningfully on FreeBSD, or is it being ported for completeness' sake alone? As a concrete example, tools built around running large language models locally typically depend on GPU compute backends (CUDA, ROCm) that FreeBSD does not have production support for. Porting the full stack for something like that adds a large surface area (build complexity, dependency chains, future maintenance burden) for a use case that mostly can't actually be exercised on the platform. The right question before adding any port in that category isn't "does this exist and is it popular," it's "does this actually work, for a real purpose, on FreeBSD specifically."
- Has the tree been checked for existing ports covering the same upstream project or functional niche? Duplicates should be avoided at intake time; it's far cheaper to redirect a submission toward an existing port (or a version bump/feature addition to it) than to deprecate one of two overlapping ports later once both have users.
- Does the maintainer already have capacity per section 2? If they're overloaded, they can't add or adopt regardless of how good the port is; someone else picks it up or it waits.
Enforcement note: There is no dedicated reviewer checking every new port commit against these criteria before it lands; in practice, whether a commit was appropriate under this section is often only noticed after the fact, if at all. Given that, any committer who identifies a new port that was added in violation of this section i.e. over-threshold maintainer, no real use case, unmaintained upstream, unworkable on FreeBSD, or an unaddressed duplicate; has the standing right to revert that addition under blanket approval, without needing to first secure individual sign-off from portmgr. This mirrors the blanket approval in section 2c: since no one is positioned to catch every violation as it happens, the committer who does catch one should be able to act on it directly rather than the violation standing simply because no formal reviewer flagged it in time.
5. Deprecation and removal criteria
A port should be considered for deprecation followed by removal after the standard deprecation window under the following conditions, unless someone steps up to fix the underlying issue:
Upstream is gone entirely: Site down, repo deleted or archived, no actively maintained fork.
Upstream is dead (code/site still exists, no activity). Before dropping, check:
- Is this a service port? Something that opens a listening socket, accepts remote connections, or otherwise has a network-facing attack surface? Dead upstream plus network exposure should be treated as a live CVE risk, not just staleness, and escalated accordingly.
- Are there multiple versions of this port in the tree where a specific version is EOL upstream even though a newer sibling is still alive? EOL'd versions get deprecated and removed on their own timeline, independent of the newer version's status.
Upstream is a rebrand/fork of a commercial product where the license requires it (e.g. an AGPL fork of a project whose license or trademark terms forbid reuse of the original name). These get renamed or removed, not left under the original name. This is a legal exposure issue as much as a maintenance one.
Additional criteria:
Known unpatched severe vulnerabilities with no maintainer response within 30 days: Deprecate regardless of upstream's general activity level.
Blocks a tree-wide transition (toolchain, crypto library, etc.) and the maintainer is unresponsive or refuses the required refactor after direct contact: becomes a portmgr-level override rather than something the transition owner has to personally absorb. If possible consider transitioning the port under infrastructure blanket approval instead.
License is incompatible or changed to something incompatible with what we can ship. Same urgency tier as the rebrand/license case above.
Superseded by a duplicate: Where two ports serve the same purpose and one is actively maintained while the other isn't, the unmaintained one is a deprecation candidate in favor of consolidating on the maintained one.
Depends on a deprecated or end-of-life library or language runtime: a port can build cleanly and still be a supply-chain risk if its dependency chain sits on something the ecosystem has moved past: Python 2 (and Python-2-only compatibility shims like py-future), GTK2, PCRE1, and similar cases are the common examples here. These don't stop working overnight, but they accumulate runtime failures, don't receive security fixes upstream, and increasingly can't be built against current toolchains at all. If a port hasn't moved to the current supported dependency (e.g. GTK3/GTK4, PCRE2, Python 3) after a defined grace period once the underlying dependency itself is deprecated in the tree, it becomes a removal candidate on that basis alone, independent of whether the port's own upstream is otherwise active. This treats "runs on a dead foundation" as its own category, separate from "upstream is dead". A port can have a perfectly alive upstream and still be a problem if it never migrates off something we're trying to get out of the tree.
A note on low-risk, low-activity ports: Absence of activity or reverse dependencies is not, by itself, sufficient grounds for removal. A single-purpose game, a desktop utility, or similar software with no network-facing surface and no dependency on anything being phased out isn't causing anyone harm by sitting quietly unchanged; "nobody's filed a bug in two years" is not the same signal as "this is a security or supply-chain risk." The criteria above are about risk and cost (network exposure, CVEs, license conflicts, blocking tree-wide work, dependence on dying infrastructure) not about popularity.
Low-activity ports that don't meet any of the risk-based criteria in this section should be left alone even if their maintainer is otherwise over threshold; the point of sections 2c and 6 is to clear real risk and real backlog and not to prune harmless software for being quiet.
6. Deprecation mechanics and best practice
Marking a port DEPRECATED: Set DEPRECATED with a clear reason **and** set EXPIRATION_DATE. Bump the port revision when doing so, and MFH (merge from head) to the current quarterly branch. The revision bump forces a reinstall/upgrade notice for users on that port, which is the mechanism that actually surfaces the deprecation to people running it. This is not just a line in the Makefile that nobody sees until removal day. If a port genuinely has active users with a real use case, this is what gives them the chance to speak up before it's gone; absent that signal, removal proceeds as planned. Don't skip the revision bump because a deprecation nobody notices isn't serving its purpose.
Check for duplicates before adding, not after. Before a new port is committed, search the tree for existing ports covering the same upstream project or functional niche. This is a normal part of the review a committer or approver should be doing anyway (see section 4), but it's worth stating explicitly here because duplicate ports are one of the more common and avoidable sources of tree bloat, and once two duplicates exist it's harder to justify removing either one. Someone's always using each of them by that point.
Standard deprecation timeline is 30 days from EXPIRATION_DATE being set to actual removal, absent a more urgent category (active CVE, license violation) that requires faster action per section 5.
Deprecating a specific version among multiple parallel versions (the common case for things like Python or PHP or Ruby, where several version-suffixed ports coexist): set EXPIRATION_DATE to the last day of the current quarterly branch, not an arbitrary 30-day mark, so the removal aligns with a branch boundary users already expect version transitions to happen around. If 30 days from now would land before the current quarterly's end date, push EXPIRATION_DATE out to the last day of the next quarterly instead, rather than compressing the deprecation window to fit inside the current one. The point is to give users a predictable, branch-aligned removal point rather than an arbitrary date that happens to fall wherever the 30-day clock landed.
7. Closing note
We can't write a rule for every workaround someone might try. Somebody will eventually find an angle none of the sections above named specifically. Structuring a port so the version-tracking tools can't see it's actually stale, adding a batch of new ports fast before the backlog has had time to show up in the numbers, or something nobody in this discussion thought of. Trying to patch the document every time someone finds a new gap isn't a real strategy.
So, portmgr isn't limited to only the mechanisms spelled out above. If someone's clearly working against the point of this policy (i.e. less overload, ports that are actually maintained, not dumping unmaintained software on everyone else) portmgr can act on that even if it's not one of the specific clauses here, and doesn't need to wait for this document to be rewritten first. The numbers and windows above exist so there's something solid to point to in the normal case, not to box portmgr in when something abnormal shows up.
If portmgr comes to a maintainer with help, a plan, or straightforward advice, take it. Don't turn it into a fight. The anti-gaming clauses in this document are aimed at someone gaming the rules while ignoring the point of them; they're not meant to be a rulebook you argue over once portmgr is actually trying to help you. Someone who takes the help, works honestly toward getting caught up, and is straight about what they can and can't manage will be treated that way in return.
That's different from disagreeing. If a maintainer thinks a decision was wrong, thinks a ratio was miscalculated, or wants someone else to look at it; that's a normal thing to do, and it's not resistance. Nobody should be penalized for pushing back in good faith. What this is actually about is the other thing: agreeing to a plan and then not doing it, quietly re-adding a port that was removed for cause, going quiet instead of engaging, or hiding behind procedure instead of actually doing the work. That kind of pattern is its own problem, separate from wherever the outdated ratio happens to sit, and can end with a commit bit being pulled. The line isn't whether you disagree but whether you're still working with the process while you do.