-
Notifications
You must be signed in to change notification settings - Fork 3.8k
Publish versioning, roadmap, and dependency policies for v2 #3215
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| # Dependency Policy | ||
|
|
||
| `mcp` is a library that lives inside other people's environments, so its dependency requirements are chosen to constrain your resolver as little as possible while still describing what the SDK actually needs. | ||
|
|
||
| ## How requirements are declared | ||
|
|
||
| * **Floors, not pins.** Every runtime dependency is a `>=` lower bound, set to the oldest version that provides what the SDK uses. There are no upper bounds unless a dependency's next major version is known to break the SDK. | ||
| * **The one exception is `mcp-types`.** The wire-types package is developed and released with `mcp` in lockstep, so `mcp` requires exactly its own version (`mcp-types==<same version>`). It is not an independent constraint on your environment; it is the other half of the SDK. | ||
| * **Environment markers instead of parallel packages** — Python-version and platform differences (`python_version`, `sys_platform`) are expressed as markers on the requirement, so one wheel serves every supported environment. | ||
| * **Optional features are extras.** Anything only some users need lives behind an extra (`mcp[cli]`, `mcp[rich]`) rather than in the base requirement set. | ||
|
|
||
| ## When a floor moves | ||
|
|
||
| A minimum version is raised only when the SDK starts relying on functionality, a fix, or an API that first appeared in that version. It is not raised because a dependency published a security advisory. The `>=` bound already lets — and expects — you to run the newest release your other constraints allow, so a higher floor would only shrink the set of environments the SDK installs into without changing what any correctly-updated environment resolves to. The SDK also does not add code to work around a vulnerability in a dependency; the fix belongs upstream and in your lockfile. ([Background](https://github.com/Kludex/uvicorn/discussions/2643) on this stance from another library that adopted it, and [python-sdk#1552](https://github.com/modelcontextprotocol/python-sdk/issues/1552).) | ||
|
|
||
| Every declared floor is exercised: CI runs the full test suite both against the locked dependency set and against a `lowest-direct` resolution, on every supported Python version, so a floor that has quietly become false fails the build rather than a user's install. | ||
|
|
||
| Raising a floor within the same major version of a dependency is a minor-release change and is called out in the release notes; see the [versioning policy](https://py.sdk.modelcontextprotocol.io/versioning/). Adding a new required runtime dependency is a maintainer decision made in an issue before the pull request, not a side effect of a feature. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 The two new policy docs answer the same question differently: DEPENDENCY_POLICY.md scopes the minor-release permission to floor raises "within the same major version of a dependency", while docs/versioning.md's non-breaking list allows any minimum-version raise (no cross-major carve-out) — and each page defers to the other as the authority. Either drop the qualifier here or add the cross-major case explicitly to versioning.md's breaking-change list, so a reader can tell whether e.g. moving to pydantic>=3 may land in a 2.X.0 release. Extended reasoning...The inconsistency. This PR adds two policy documents that both make a normative statement about when a dependency floor raise may ship in a minor release, and they don't agree on scope. Why the qualifier matters. In a document whose stated purpose (per the PR description) is to publish commitments rather than descriptions — read by the SEP-1730 tier assessment — a scoped qualifier reads as deliberate. "Within the same major version" plainly implies that raising a floor across a dependency's major version is a different, larger kind of change. That reading is reinforced by the same file already treating dependency majors specially ("no upper bounds unless a dependency's next major version is known to break the SDK"). versioning.md, by contrast, admits any needed-functionality floor raise into a minor with no such carve-out. Why a reader can't resolve it. The deferral is circular: versioning.md says "the dependency policy covers the first", and DEPENDENCY_POLICY.md says "see the versioning policy". Neither page claims precedence, so neither text settles which rule governs the cross-major case. Concrete walk-through. Suppose the SDK decides it needs a feature that first ships in pydantic 3.0, so the floor moves from Strictly speaking the two statements are logically compatible — asserting the same-major case is minor doesn't assert the cross-major case is major; it's implicature, not contradiction. But either the qualifier is intentional (and versioning.md's unconditional wording is then wrong) or it isn't (and the qualifier is misleading scope-narrowing). One of the two sentences needs an edit either way. Fix. Pick one: (a) if cross-major floor raises are meant to be minor-eligible, drop "within the same major version" from DEPENDENCY_POLICY.md line 18; or (b) if they're meant to wait for the next |
||
|
|
||
| ## Automated updates | ||
|
|
||
| [Dependabot](https://github.com/modelcontextprotocol/python-sdk/blob/main/.github/dependabot.yml) opens monthly, grouped pull requests for the `uv` lockfile and for GitHub Actions, with a 14-day cooldown on newly published versions. These refresh the versions the SDK is developed and tested against (`uv.lock`); they never change the requirements published to PyPI, which move only under the rules above. | ||
|
|
||
| ## Security in the SDK itself | ||
|
|
||
| Vulnerabilities in the SDK's own code — as opposed to its dependencies — follow the reporting process and response commitments in [SECURITY.md](https://github.com/modelcontextprotocol/python-sdk/blob/main/SECURITY.md). | ||
|
Check warning on line 26 in DEPENDENCY_POLICY.md
|
||
|
Comment on lines
+24
to
+26
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 The new "Security in the SDK itself" section says SDK vulnerabilities "follow the reporting process and response commitments in SECURITY.md", but SECURITY.md contains no response commitments — only a supported-versions table, the advisory reporting channel, and a what-to-include checklist. Either soften to "the reporting process in SECURITY.md" (matching how docs/versioning.md in this same PR accurately phrases the identical pointer), or add actual response commitments (acknowledgement window, triage/fix timeline) to SECURITY.md. Extended reasoning...The inaccuracy. DEPENDENCY_POLICY.md line 26 states that vulnerabilities in the SDK's own code "follow the reporting process and response commitments in SECURITY.md". SECURITY.md (unchanged by this PR) contains exactly three things: a supported-versions table, a "Reporting Security Issues" section (use GitHub Security Advisories; do not use public issues/discussions/PRs), and a "What to Include" checklist. There is no response commitment of any kind — no acknowledgement window, no triage SLA, no fix or disclosure timeline. The phrase "To help us triage and respond quickly" is a request to the reporter, not a commitment by maintainers.\n\nWhy the pointer dead-ends. The only response-time commitment this PR publishes anywhere is CONTRIBUTING.md's new triage table, where P0 (which includes High/Critical-severity security issues) is "resolved within 7 days". But that table explicitly governs the public issue tracker — and the very same section (plus SECURITY.md itself) says security reports must not go through the issue tracker. So a security reporter following the chain has no committed response at all: the private channel makes no commitments, and the channel with commitments is the one they're told not to use.\n\nThe accurate wording exists in this same PR. docs/versioning.md phrases the identical cross-reference correctly: "The security-specific version of this table, and how to report a vulnerability, is in SECURITY.md" — describing what the file actually contains, without asserting commitments that don't exist. That contrast shows this is a wording slip in DEPENDENCY_POLICY.md, not a deliberate claim.\n\nWhy it matters for this PR specifically. Per the PR description, these pages are written as "commitments rather than descriptions" for the SEP-1730 tier assessment. A tier assessor or vulnerability reporter who follows the pointer to learn what response to expect finds nothing, and can reasonably conclude the policy chain is broken — the one failure mode a policy-publication PR is trying to avoid.\n\nStep-by-step proof. (1) A researcher finds a vulnerability in the SDK's own code and reads DEPENDENCY_POLICY.md's "Security in the SDK itself" section, which promises "response commitments in SECURITY.md". (2) They open SECURITY.md and read it in full: supported-versions table → GitHub Security Advisory channel → what-to-include checklist. No commitment appears. (3) They check whether commitments live elsewhere and find CONTRIBUTING.md's P0 row ("resolved within 7 days") — but its scope is issue-tracker labels, and both CONTRIBUTING.md and SECURITY.md direct security reports away from the issue tracker. (4) The promised commitments do not exist anywhere the reporter is allowed to go. One charitable reading — that the supported-versions table ("which lines receive security fixes") is itself a "response commitment" — doesn't hold: that is a support-scope statement, not what a reporter would understand as a response commitment (what happens, and when, after filing).\n\nFix. Either (a) soften the sentence to "…follow the reporting process in SECURITY.md", a two-word deletion that makes it accurate, or (b) if response commitments are meant to exist for the tier assessment, add them to SECURITY.md (acknowledgement window, triage SLA, fix/disclosure timeline) so the pointer becomes true. Docs-only; nothing breaks at merge, hence not blocking. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,10 +2,17 @@ | |
|
|
||
| ## Bumping Dependencies | ||
|
|
||
| [`DEPENDENCY_POLICY.md`](DEPENDENCY_POLICY.md) says *when* a bound should | ||
| move; this is the mechanics. | ||
|
claude[bot] marked this conversation as resolved.
|
||
|
|
||
| 1. Change the dependency version in `pyproject.toml`. The root `mcp` project's | ||
| runtime dependencies are dynamic and live under | ||
| `[tool.hatch.metadata.hooks.uv-dynamic-versioning].dependencies`. | ||
| 2. Upgrade lock with `uv lock --resolution lowest-direct` | ||
| 2. Regenerate the lock with `uv lock` (or `uv lock --upgrade-package <package>` | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P3: The lowest-direct CI run does write a temporary lockfile: its matrix command is Prompt for AI agents |
||
| to move just that package's locked version). The committed `uv.lock` is a | ||
| normal, newest-allowed resolution; the `lowest-direct` resolution that | ||
| proves the floors still work is applied only by its CI matrix leg at test | ||
| time and never written to the lock. | ||
|
|
||
| ## Release lines | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| # Roadmap | ||
|
|
||
| The SDK's job is to implement the MCP specification, so its roadmap is organized by specification revision: one GitHub project board per revision, each item an issue or pull request you can follow. This page names the board that is current, what remains open on it, and the maintenance stance for the previous major. | ||
|
|
||
| ## The 2026-07-28 revision — shipped, with follow-ups | ||
|
|
||
| v2 implements the [2026-07-28 specification](https://modelcontextprotocol.io/specification/2026-07-28) (and negotiates back to every earlier revision — see [Protocol versions](protocol-versions.md)); **[What's new in v2](whats-new.md)** is the tour of what that meant for the SDK. | ||
|
|
||
| * Board: **[python-sdk · 2026-07-28 spec](https://github.com/orgs/modelcontextprotocol/projects/42)**, tracking issue [#2891](https://github.com/modelcontextprotocol/python-sdk/issues/2891). | ||
| * Cross-SDK view: [2026-07-28 Spec Implementation](https://github.com/orgs/modelcontextprotocol/projects/41) tracks the same revision across all official SDKs. | ||
|
|
||
| Open on that board: | ||
|
|
||
| * **Capabilities API and the `server/discover` handler** — the last core item still in progress ([#2896](https://github.com/modelcontextprotocol/python-sdk/issues/2896)). | ||
|
|
||
| ## Extensions and optional client auth not yet implemented | ||
|
|
||
| The 2026-07-28 revision moved some functionality out of the core protocol into named extensions, and defines client-side auth mechanisms an SDK may support. The ones this SDK does not implement yet are exactly the entries in the conformance suite's expected-failures baseline, [`.github/actions/conformance/expected-failures.yml`](https://github.com/modelcontextprotocol/python-sdk/blob/main/.github/actions/conformance/expected-failures.yml) — that file is grouped by SEP and each entry is removed as the corresponding work lands, so it is the live burn-down list: | ||
|
|
||
| * **Tasks extension** (`io.modelcontextprotocol/tasks`, [SEP-2663](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/seps/2663-tasks-extension.md)) — deferred at 2.0 because the 2026-07-28 design is wire-incompatible with the earlier in-core Tasks; tracked in [#2806](https://github.com/modelcontextprotocol/python-sdk/issues/2806). | ||
| * **DPoP-bound access tokens** ([SEP-1932](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1932)) in the OAuth client. | ||
| * **The workload-identity `jwt-bearer` grant** in the OAuth client. | ||
|
|
||
| None of these gates conformance today — extension scenarios are informational in the tier scoring — but each is a real gap for anyone who needs the feature, and they are the current queue. | ||
|
|
||
| ## Continuous work | ||
|
|
||
| * **Conformance** — every pull request and every push to `main` runs the [conformance suite](https://github.com/modelcontextprotocol/conformance) as both server and client, against the released revisions and against the 2026-07-28 wire specifically; adopting each new harness release and reconciling its baseline is routine. | ||
| * **The next specification revision** — draft-only wire changes are tried behind the draft protocol version before they are final, and land in a release once the revision ships; the SDK targets releasing support alongside each new specification version. | ||
| * **Everything else** — the [issue tracker](https://github.com/modelcontextprotocol/python-sdk/issues) is the source of truth for bugs and smaller features; `P0`–`P3` labels carry priority. | ||
|
|
||
| ## The previous major | ||
|
|
||
| `v1.x` is a maintenance line: critical bug fixes and security fixes only, no new features. Its documentation stays available at [/v1/](https://py.sdk.modelcontextprotocol.io/v1/), the support terms are in [Versioning and support policy](versioning.md#supported-release-lines), and the path off it is the **[Migration Guide](migration.md)**. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| # Versioning and support policy | ||
|
|
||
| This page states what a version number of the `mcp` package promises you: which changes can arrive in a minor release, which are held for the next major, how deprecations are announced, and how long each release line is supported. | ||
|
|
||
| ## The version number | ||
|
|
||
| Releases follow [Semantic Versioning](https://semver.org/) semantics, written in [PEP 440](https://peps.python.org/pep-0440/) syntax: | ||
|
|
||
| * **`2.X.Y`** — the version comes from the git tag; there is no version field to edit. | ||
| * **`X` (minor)** — new functionality and every non-breaking change. | ||
| * **`Y` (patch)** — bug fixes only. | ||
| * **The leading `2` (major)** — the only place a breaking change to the public API can land. | ||
| * **Pre-releases** are cut from `main` as `2.X.YaN` (alpha), `2.X.YbN` (beta), and `2.X.YrcN` (release candidate). Installers select a pre-release only when a requirement asks for one explicitly — an exact pin, a specifier that itself names a pre-release version (such as `mcp>=2.1.0b1`), or `--pre` — so an unpinned `pip install mcp` always lands on a stable release. | ||
|
|
||
| `mcp` and its wire-types package [`mcp-types`](https://pypi.org/project/mcp-types/) release in lockstep at the same version: each `mcp` release requires exactly the matching `mcp-types` (`mcp-types==2.X.Y`). | ||
|
|
||
| ## What the public API is | ||
|
|
||
| The compatibility promise covers the public API: | ||
|
|
||
| * every name exported by `mcp` (its `__all__`) and by `mcp_types`, | ||
| * the import paths, classes, functions, and parameters documented on this site and in the [API Reference](api/mcp/index.md), | ||
| * documented behavior of those APIs. | ||
|
|
||
| It does not cover names beginning with an underscore, modules and attributes that appear nowhere in the documentation, or the exact text of log lines, warnings, and exception messages (their *type* and the documented conditions that raise them are covered; their wording is not). Depending on one of those is depending on an implementation detail that may change in any release. | ||
|
|
||
| Two labels mark APIs that sit outside the promise while they settle: | ||
|
|
||
| * **Provisional** — shipped and supported, but the signature or semantics may still change in a minor release. The middleware chain is the current example, and its documentation says so. | ||
| * **Experimental** — behind an explicit opt-in and expected to change; treat it as a preview. | ||
|
|
||
| ## What counts as a breaking change | ||
|
|
||
| These wait for the next major version: | ||
|
|
||
| * removing or renaming a public name, | ||
| * changing a signature so that a call that worked stops working (a removed or reordered parameter, a newly required argument, a narrowed accepted type), | ||
| * changing a return type, a raised exception type, or documented behavior in a way existing callers would notice, | ||
| * removing a documented import path, extra, or CLI command. | ||
|
|
||
| These do not, and can ship in a minor release: | ||
|
|
||
| * new functions, parameters with defaults, classes, fields, and enum members, | ||
| * changes to provisional or experimental APIs, | ||
| * new deprecation warnings, and the eventual removal of a protocol feature the specification has retired (see [Deprecations](#deprecations)), | ||
| * raising a dependency's minimum version when the SDK needs newer functionality, or dropping a Python version that upstream has ended support for — both called out in the release notes (the [dependency policy](https://github.com/modelcontextprotocol/python-sdk/blob/main/DEPENDENCY_POLICY.md) covers the first), | ||
| * bug fixes, including fixes that make the SDK match documented or specified behavior it should have had all along. | ||
|
|
||
| When a fix is arguably both a bug fix and a behavior change, the deciding question is whether reasonable code written against the *documented* behavior breaks. If it does, the change is breaking. | ||
|
|
||
| ## Deprecations | ||
|
|
||
| There are two kinds, warned differently on purpose. | ||
|
|
||
| **SDK API deprecations** — a name or parameter this SDK is retiring. The API keeps working, marked with [`typing_extensions.deprecated`](https://typing-extensions.readthedocs.io/en/latest/#typing_extensions.deprecated), so static type checkers flag every call site and Python emits a `DeprecationWarning` at runtime. A deprecated API survives at least one minor release with its warning in place, and is removed only in a major version: something deprecated during 2.x is not removed before 3.0. | ||
|
Check warning on line 55 in docs/versioning.md
|
||
|
Comment on lines
+53
to
+55
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 The categorical claim that SDK API deprecations are "marked with Extended reasoning...What the page promises vs. what the SDK does. The new Deprecations section states categorically that SDK API deprecations are "marked with [ The counterexample. No warning mechanism exists. Grepping Step-by-step proof. (1) A user reads this policy page and audits per its implied procedure: run the suite with warnings-as-errors and type-check with pyright, expecting every deprecated SDK API to surface. (2) Their code does Why the named mechanism cannot cover this class. Fix (either side works). Docs-side: qualify the sentence, e.g. "functions, methods, and classes are marked with Severity. Docs-only imprecision in a policy page whose substance is the accuracy of these commitments — worth fixing in this PR since it introduces the claim, but nothing breaks at merge, so it is not blocking. All four verifiers confirmed independently; none refuted. |
||
|
|
||
| **Protocol deprecations** — a feature the MCP specification has retired (for example the SEP-2577 set in the 2026-07-28 revision). The SDK keeps implementing these through the specification's deprecation window, but what still works depends on the revision a connection negotiated: on a connection speaking an older revision they behave as before; on a 2026-07-28 connection a retired feature may have no wire support left at all (server-initiated sampling and roots have no back-channel to travel over, and `ping` no longer exists), so the call warns and then fails. Either way the call site warns with `MCPDeprecationWarning`, a `UserWarning` subclass, so the warning is visible by default rather than hidden the way `DeprecationWarning` is outside `__main__`. **[Deprecated features](deprecated.md)** lists every one, exactly what happens on each kind of connection, its replacement, and how to silence the warning when you genuinely serve older clients. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P3: The linked page does not yet describe each modern-connection outcome as claimed: protocol logging can be silently dropped unless the request opted in, not simply warn then fail. Add that per-revision behavior to Prompt for AI agents |
||
|
|
||
| ## Supported release lines | ||
|
|
||
| Two lines are maintained, and only the newest release of a line receives fixes: | ||
|
|
||
| | Line | Branch | Receives | | ||
| | --- | --- | --- | | ||
| | 2.x — current stable | `main` | bug fixes, security fixes, new features | | ||
| | 1.x — maintenance | [`v1.x`](https://github.com/modelcontextprotocol/python-sdk/tree/v1.x) | critical bug fixes and security fixes | | ||
|
|
||
| Older 1.x releases and all pre-releases are unsupported. The security-specific version of this table, and how to report a vulnerability, is in [SECURITY.md](https://github.com/modelcontextprotocol/python-sdk/blob/main/SECURITY.md). Still on 1.x? Its documentation is at [/v1/](https://py.sdk.modelcontextprotocol.io/v1/), and a `<2` upper bound on your `mcp` requirement keeps an unpinned resolve on that line until you migrate. | ||
|
|
||
| Python versions are supported from the version in the package's `requires-python` up to the newest CPython release the test suite runs against; support for a Python version ends only after that version's upstream end-of-life. | ||
|
|
||
| ## Where changes are announced | ||
|
|
||
| * **Release notes** — every release publishes curated notes on [GitHub Releases](https://github.com/modelcontextprotocol/python-sdk/releases): highlights, anything known-incomplete, and a full change list. Pre-releases say what changed since the previous pre-release. | ||
| * **The migration guide** — every breaking change between majors is documented in **[Migration Guide](migration.md)** with before-and-after code; a change is not merged for a major release without its entry. | ||
| * **The `breaking change` label** — pull requests that make a breaking change carry it, so the set is queryable ahead of a major release. | ||
| * **Deprecation warnings** — as above, one release of warning at minimum before an SDK API is removed. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 The closed label taxonomy this section publishes ('one type, one status, one priority') is contradicted by the repo's own labels: pre-existing line 51 of this same file references a
needs maintainer actionstatus label the enumeration omits, and the issue-template automation applies labels outside it —feature-request.yamlauto-appliesfeature request(notenhancement, which no template applies), andbug.yamlauto-appliesneed confirmation(no 's', mismatching the documentedneeds confirmation) while applying nobugtype label at all. Since this PR canonizes the taxonomy as the SEP-1730-measured contract, either align the enumeration and the templates'labels:fields with it, or document how the actual labels map onto it.Extended reasoning...
What the inconsistency is. The new Issue Triage section (CONTRIBUTING.md:59) publishes a closed label taxonomy as a normative contract: "The labels follow the shared MCP SDK taxonomy: one type (
bug,enhancement,question), one status (needs confirmation,needs repro,ready for work,good first issue,help wanted), and — once actionable — one priority". Two independent sources in the same repo contradict that enumeration: the same document's own pre-existing text, and the issue-template automation that actually applies labels to every new issue.Contradiction 1 — within CONTRIBUTING.md itself. Line 51 (pre-existing) says "Issues labeled
needs confirmationorneeds maintainer actionare not ready for work" — treatingneeds maintainer actionas an in-use status label, grouped withneeds confirmation(which is in the enumeration). But the closed status set published eight lines later omits it, andneeds maintainer actionappears nowhere else in the repo. A reader cannot map the label the document itself uses onto the taxonomy the document declares every issue follows.Contradiction 2 — the issue-template automation. Verified in the checkout:
.github/ISSUE_TEMPLATE/feature-request.yamlline 3:labels: ["feature request"]— a label absent from the taxonomy, whose type label for this class isenhancement. No template appliesenhancement, so it is absent from every template-created feature issue..github/ISSUE_TEMPLATE/bug.yamlline 3:labels: ["need confirmation"]— no 's', mismatching both the pre-existing line 51 and the new taxonomy at line 59 (both spell itneeds confirmation). One of the two spellings is not a real label. The template also applies nobugtype label at all, so a template-filed bug starts with only a (misspelled) status label.question.yaml(labels: ["question"]) is the only template consistent with the taxonomy — confirming the mismatches are specific and real, not a misreading.Step-by-step proof. (1) A user files a bug via the template; GitHub applies
need confirmationand nothing else. (2) A triager or the SEP-1730 tier check follows the just-published contract and querieslabel:"needs confirmation"or expects one type label per issue — the query misses the issue, and the one-type invariant is already violated at creation. (3) Similarly, a contributor reading the taxonomy querieslabel:enhancementfor feature work and finds nothing, because every template-filed feature request carriesfeature requestinstead. The contract published by this PR is falsified by the repo's own automation on every template-filed issue.Why this belongs in this PR. The templates and line 51 pre-date the PR, but the PR is what introduces the closed enumeration and canonizes it as the taxonomy the SEP-1730 tier assessment measures — the contradiction only becomes a contradiction once this text merges. This is a policy-docs PR whose substance is textual precision, so reconciling is squarely in scope.
How to fix. Either (a) align reality with the taxonomy — add
bug+needs confirmationtobug.yaml'slabels:, changefeature-request.yamltoenhancement, and either addneeds maintainer actionto the status enumeration or rewrite line 51 to use a listed label — or (b) keep the labels as they are and add a sentence mapping them onto the taxonomy. Option (a) is a few one-line edits and makes the published contract true.Impact/severity. Nothing breaks at merge — this is a docs/process inconsistency, so nit severity. All six verifier confirmations (three per source bug) agreed on the facts and on nit; there were no refutations.