diff --git a/.claude/skills/retiring-server-versions/SKILL.md b/.claude/skills/retiring-server-versions/SKILL.md new file mode 100644 index 0000000000..92a4496c41 --- /dev/null +++ b/.claude/skills/retiring-server-versions/SKILL.md @@ -0,0 +1,400 @@ +--- +name: retiring-server-versions +description: >- + Use when raising the minimum supported MongoDB server version in this repository (e.g. EOL'ing an old server + version) and needing to find and remove now-dead version-gated tests, prose, and pseudocode across the whole + specifications repo. +--- + +# Retiring Server Versions + +## Overview + +Raising the minimum-supported-server-version floor doesn't just delete a few "if version < X" branches — it leaves +behind scattered dead conditionals, vacuous qualifiers, unrunnable tests, and stale prose across the whole repository. +The single biggest failure mode is scoping the cleanup too narrowly (to files already touched, or to obvious `< X.Y` +patterns) and then discovering — repeatedly, across multiple review rounds — that reviewers keep finding whole files and +whole *categories* of miss that the first pass never looked for. + +**Core principle:** Establish fix/leave criteria explicitly before editing, sweep the *whole* repo (not just +already-changed files) with a wide pattern net, verify judgment calls against ground truth (real driver source), and +expect to re-sweep every time a reviewer finds a new *shape* of miss — not just patch the one instance. + +## When to Use + +- You're working a ticket to remove old server version references (e.g. `DRIVERS-XXXX` "Remove pre-N.N references from + specs and tests"). + - Only expected once *all* drivers have dropped support for the old server (e.g. `DRIVERS-XXXX` "Mark Server version + N.N as EOL" is complete). Drivers that track this repo via a git submodule pick up test deletions as soon as they + bump the submodule, so removing tests ahead of the EOL silently drops coverage for drivers that still support the + old version. +- You're removing dead version-gated conditionals, fallback branches, or "only applies to old versions" prose. +- A reviewer keeps finding pre-floor references you thought you'd already cleaned up. + +**Not for:** routine deprecation of a single feature/API (that's a normal spec change); one-off version bumps with no +conditional logic to clean up. + +## Step 1: Determine the Old Floor and Confirm Scope + +Don't assume you know what the previous minimum version was, and don't assume this is the first such cleanup. + +- Grep git log for the most recent prior cleanup of this kind, e.g. + `git log --all --oneline -i --grep="pre-.*version\|remove.*pre-\|minimum.*server.*version"` (adjust to match this + repo's actual commit-message conventions), to find the last "remove pre-X.Y references" effort and see what floor it + targeted. +- State the inferred old floor to the operator explicitly and get confirmation before scoping any work — don't proceed + on an assumption. +- The new floor is normally given by the task/ticket (e.g. `DRIVERS-XXXX` "remove pre-4.4 references"). If it isn't + stated, ask the operator directly — don't infer it as simply "the next version after the old floor." Server versions + don't always retire in strict sequence, and guessing wrong scopes the entire cleanup incorrectly. +- If the inferred old floor is more than one version below the new floor (e.g. the last cleanup only reached pre-4.0, + but the floor is now 4.4 — meaning pre-4.2 was never actually done), don't bundle multiple version retirements into + one pass. Do them one at a time (pre-4.2 first, then pre-4.4 as its own follow-up), each as its own ticket/PR. + Bundling makes it impossible to tell which removal was justified by which version bump, and produces a much larger, + harder-to-review diff. + +## Step 2: Agree on Fix/Leave Criteria Before Editing + +Before editing anything, get explicit agreement on two buckets. Getting this wrong costs far more time than getting it +right up front. + +**FIX (dead weight at the new floor):** + +- Normative MUST/MUST NOT behavior, tests, or pseudocode conditioned on a version/wire-version below the new floor +- "X.Y or higher/later/+" qualifiers — check both *below* the new floor AND *exactly at* it. A "4.2 or higher" qualifier + is just as dead as "3.6 or higher" once 4.2 *is* the floor. This exact-at-floor case is the most commonly missed + pattern — treat it as a first-class search target, not an afterthought. +- Fallback/legacy branches for capabilities/fields that can no longer be absent (e.g. a hello-response field that's now + always present) +- `Minimum Server Version` metadata line (a `- Minimum Server Version: X.Y` bullet under the doc's H1, not YAML + frontmatter) below the new floor (repo convention: remove the line entirely, don't bump it to the new floor, unless + the spec's own minimum is a genuinely feature-specific requirement, e.g. CSFLE's 4.2) +- Unrunnable test instructions (requires a server version that no longer exists, or a mechanism removed *before* the + floor — e.g. `MONGODB-CR`, removed in 4.0, below a 4.2 floor) + +**LEAVE (keep as-is):** + +- Historical narrative ("Version X introduces...", Abstracts, Motivation/Design Rationale/Q&A sections, `@since` + annotations) +- Version boundaries between two versions that are **both still supported** (e.g. "4.2 to 5.0" when the floor is 4.2 — + this is a real, meaningful boundary, not vacuous) +- `minServerVersion` values in `runOnRequirements` test gates, regardless of value — a floor below the new minimum is + trivially satisfied by every supported server, so the test keeps running fine; leave it (confirm this policy with + the repo owner; it was the explicit ruling last time). **This does NOT extend to `maxServerVersion`** — see Step 4's + "Unrunnable YAML tests and files" section; a `maxServerVersion` below the new floor makes the test permanently + unrunnable and must be handled as a FIX, not left alone. +- Features/commands removed **at** the new floor, not before it (e.g. `copydb` removed exactly at 4.2 is out of scope + for a "remove pre-4.2 references" pass — same bucket as anything at/above the floor) +- Deliberately-invalid schema fixtures under `source/unified-test-format/tests/invalid/`. These hardcode nonsense + versions (`maxServerVersion: 0`, `minServerVersion: "1.2.3.4"`) precisely to prove the schema rejects them. Because + they sit below *every* conceivable floor, they match the `maxServerVersion` sweep in Step 4 on every retirement, + forever, and look exactly like dead weight — but deleting them breaks schema validation. Verified: they surface only + in the `maxServerVersion` sweeps, not in the numeric or table-cell searches. + +Capture ambiguous cases as a third "borderline" bucket for human triage rather than guessing either direction. + +## Step 3: Sweep the Whole Repo — Never Just the Diff + +The single most common failure: scoping the search to `git diff --name-only`. Every review round found files +*outside* that diff — including specs never touched at all in earlier passes (`uri-options.md`, `max-staleness.md`, +`server-selection.md`, `OP_MSG.md`). Always grep the entire repository under `source/`, every pass, even (especially) on +follow-up passes after the first round looked done. + +## Step 4: Cast a Wide Pattern Net + +Each of the following is a distinct *shape* of hit that requires its own search — do not assume one regex catches all of +them. Read 10+ lines of context around every hit before classifying it (most hits in this repo are legitimate historical +narrative, not dead code); never classify from the grep line alone. + +Bias every pattern below toward recall over precision: a false positive costs a few extra seconds of reading and +discarding; a false negative silently drops a real dead reference from the sweep, which is exactly the failure mode this +whole skill exists to prevent. Some commands below will also match a patch version (e.g. `< 4.2.9`, `pre-4.2.9`) — don't +try to exclude that with lookarounds; it's a feature, not noise, since a patch-specific dead reference (e.g. a bug that +only affected `4.2.0`-`4.2.5`) is exactly the kind of hit you need to see and classify, not filter out. Multi-digit +collisions like `14.2` are excluded by the *left*-anchored patterns (`<`, `pre-`, since the anchor must sit immediately +before the digit) but NOT by the *right*-anchored ones (`+`, `or higher` — e.g. `14.2+` and `14.2 or higher` both still +match, since nothing constrains what precedes the number). No shipped MongoDB release creates such a collision today, +but that expires once the server reaches double-digit majors — a `5.0` retirement collides with `15.0`, a `6.0` one with +`16.0`. Don't assume all four patterns are equally guarded; they aren't. If the colliding version exists by the time you +read this, add an explicit left guard — `(^|[^0-9])` — to the right-anchored patterns only. + +### Numeric comparisons + +This is one single-floor pass (see Step 1 — don't bundle separate floor-raises together), and you already know both +numbers involved: the old floor being retired (e.g. 4.2) and the new floor (e.g. 4.4). Don't search broadly across every +historical version; that mostly re-surfaces content from *previous* retirements that was already correctly classified as +historical narrative last time (tested: a broad `< ?[0-9]+\.[0-9]+` search here returns roughly half noise — unrelated +numeric comparisons, other libraries' version checks, and legitimate current boundaries well above the new floor — while +the scoped version below loses no real hits). Scope the search to just the two numbers in play, and include `<=` +alongside `<` — a bare `< ?` anchor misses `<=` entirely, and this repo has a real, currently-live example +(`MongoDB \<= 4.2, a monitor uses the Polling Protocol...` in `server-monitoring.md`) that only the `<=?` form catches. +Note the backslash in that quote is really on disk, not a typo: `mdformat` escapes a bare `<` in prose as `\<`. The +patterns below still match it (`<=` is a substring of `\<=`), but don't anchor a pattern to a character immediately +preceding `<`, and expect `\<` in the grep output. + +``` +grep -rnE "<=? ?(4\.2|4\.4)\b" source --include="*.md" +grep -rnE "\bpre-?(4\.2|4\.4)\b" source --include="*.md" +``` + +(substitute your own old-floor/new-floor numbers for `4\.2`/`4\.4`). The first is nearly clean. The second is noisier — +most of its hits are `## Changelog` entries like "Remove pre-4.2 version references," which are never in scope (see Step +2). Both commands can surface Changelog hits (e.g. "2020-01-10: Error if hint specified... for servers < 4.2"); skip +anything inside a Changelog section before reading further, for either command. The English-language equivalents ("older +than", "prior to") are much lower precision as bare phrases — tested samples were mostly unrelated to server versions at +all (e.g. "prior to each test run", "prior to garbage collection") — so treat them as a supplementary spot-check, not a +primary tool. + +### "X.Y or higher/+" qualifiers — check both below the floor and at it + +Same reasoning as above — scope to the two numbers in play, not every historical version: + +``` +grep -rnE "(4\.2|4\.4)\+" source --include="*.md" +grep -rnE "(4\.2|4\.4) or (higher|later|newer|greater|above)" source --include="*.md" +``` + +(substitute your own old-floor/new-floor numbers). Tested clean in this repo; the broad, number-agnostic version returns +several times as many hits, and the extra ones are entirely legitimate current-and-above-floor content like "server +7.0+" or "9.0+", not dead weight for this retirement. Classify every hit against the new floor explicitly — a hit whose +number equals the new floor exactly (e.g. "4.4 or higher" when 4.4 *is* the new floor) is just as dead as one below it, +but easy to overlook because the number looks current at a glance. Treating "at the floor" hits as their own deliberate +pass, not an afterthought, is the single most commonly skipped step. + +### Wire-version proxies, spelled out + +Wire version numbers are too short to grep for bare — a bare `9` returns hundreds of unrelated line/port/byte-count +hits. Anchor to the word instead, and search both wire versions in play (the retiring version's and the new floor's), +mirroring the two-number scoping used everywhere else in this step: + +``` +grep -rnE "(wire ?[Vv]ersion|[Mm]ax[Ww]ireVersion|[Mm]in[Ww]ireVersion)['\"]? *(is |>=? ?|<=? ?|of |as )?(8|9)\b" source --include="*.md" +``` + +Substitute the two wire versions you're retiring across (here `8` = server 4.2 and `9` = server 4.4). Keep the trailing +`\b`: it's what stops `9` from matching inside `93`, and it matters more, not less, for multi-digit wire versions. + +Zero hits is a common and usually genuine result — only a handful of wire numbers are spelled out in prose anywhere in +this repo. Before trusting a zero, confirm your two numbers rather than your regex: **the canonical table can lag the +server.** As of this writing it stops at 8.0 (wire 25) and has no row for 9.0 at all, even though ~22 files already +reference 9.0 — so an 8.0 retirement cannot look its new floor up there. If your version is missing from the table, take +the number from the server's `releases.yml` (linked at the bottom of that file) and consider adding the missing row as a +separate PR; do not extrapolate it. + +**Never compute a wire version from a server version — always look it up** in the canonical table at +[`source/wireversion-featurelist/wireversion-featurelist.md`](../../../source/wireversion-featurelist/wireversion-featurelist.md). +Two traps make arithmetic actively wrong from 5.0 onward: + +- **They go multi-digit.** Server 4.4 is wire 9, but 5.0 is wire **13** — so a pattern written as a single trailing + digit stops working the moment you retire 4.4 or later. +- **The sequence has gaps.** 4.4 → 5.0 skips wire 10–12, and 6.2 → 7.0 skips wire 20. There is no reliable offset to + add; since server 5.1 the wire version is derived from the number of releases since 4.0, not from the version + number. + +That table file is itself out of scope for this cleanup — it's inherently a version-mapping reference, not a version +gate. + +### Table cells / structured data + +Prose-oriented regexes miss a bare version number sitting alone in a table column. Anchor to the pipe delimiter instead, +repo-wide like every other search in this step (not scoped to one file you already suspect — that defeats Step 3's +"sweep the whole repo" rule): + +``` +grep -rnE '\|\s*(4\.2|4\.4)' source --include="*.md" +``` + +(substitute your own retiring/new-floor numbers, exactly as in the patterns above — don't broaden this to a digit range +like `[2-4]\.[0-9]`. A range re-surfaces every historical version's table rows, which Step 4's opening argument already +rejects, and it silently matches nothing once you're retiring 5.0 or later.) Expect +`wireversion-featurelist/wireversion-featurelist.md` to dominate the hits; it is out of scope by definition (see +"Wire-version proxies" above), so skip it and read the rest. + +### Comments inside YAML test files + +Apply every pattern above to `.yml` comments too, not just markdown prose — it's the same dead content in a different +file type. Extend `--include` accordingly (e.g. `--include="*.yml"`) when re-running any of the searches above. + +### Unrunnable YAML tests and files + +This is a distinct action, not just another search pattern — `minServerVersion` and `maxServerVersion` don't behave the +same way under a floor raise. A `minServerVersion` below the new floor is harmless (trivially satisfied by every +supported server; see Step 2 LEAVE). A `maxServerVersion` below the new floor is the opposite: the test can **never run +again against any supported server**, and must be removed — this is the single largest category of work in a typical +retirement (the original pre-4.2 cleanup this skill is modeled on deleted ~26 whole files and ~28 individual tests this +way). + +`runOnRequirements` can appear at two levels, and the correct action differs: + +- **File-level** (top of the file, before any `tests:` key) — the *entire file* is dead. Delete both the `.yml` and its + paired `.json`. +- **Per-test** (nested under one entry inside the `tests:` array) — only *that test* is dead; remove just that entry and + leave the rest of the file alone. + +Find file-level candidates first (check these before the noisier per-test search, since a whole dead file makes any of +its per-test hits moot): + +``` +for f in $(grep -rl "maxServerVersion" source --include="*.yml"); do + awk '/^tests:/{exit} /maxServerVersion/{print FILENAME": "$0}' "$f" +done +``` + +Then find per-test candidates in the remaining files: + +``` +for f in $(grep -rl "maxServerVersion" source --include="*.yml"); do + awk '/^tests:/{flag=1} flag && /maxServerVersion/{print FILENAME": "$0}' "$f" +done +``` + +Both commands print *every* `maxServerVersion` line, not just dead ones — including ones at or above the new floor (e.g. +`maxServerVersion: "7.0.99"`), which are never in scope (see Step 2 LEAVE: at/above the floor is fine). Expect the raw +hit list to be several times larger than the real one; read each hit's value against the new floor to classify it. Only +after that filtering does a real file-level set emerge (e.g. `crud/tests/unified/deleteOne-hint-serverError.yml` with a +top-level `maxServerVersion: 4.3.3`), and note that a per-test hit rarely means the whole file is dead (e.g. +`insertOne-serverErrors.yml`, where only a minority of its tests are individually gated dead). + +Unlike numbered markdown prose tests (Step 6 — mark `**Removed**`, never delete-and-renumber), these unified-test +entries are matched by description string, not by ordinal position, and aren't referenced by index across drivers — so +it's safe to delete them outright, no marker needed. Before deleting from a `.yml`, check whether it has a `.template` +source (see "Generated + `.template` file pairs" below) — if so, edit the template and regenerate rather than +hand-deleting the generated file directly. + +### Illustrative examples citing the current floor + +An example error message or scenario can hardcode today's specific numbers (e.g. "wire version 8... MongoDB 4.2") and go +stale when the floor moves, even though it isn't a live conditional. A bare search for the old floor's number is far too +noisy to use directly; narrow it to quoted/blockquote text, which is where hardcoded examples usually live: + +``` +grep -rnE '"[^"]*4\.2[^"]*"|>\s*".*4\.2' source --include="*.md" +``` + +(adjust `4\.2` to the old floor). This heuristic is not exhaustive — an example can appear outside quotes too — so still +read context around any bare-floor-number hits you find through other means; it just gives you a fast, low-noise +starting point instead of triaging the full bare-number hit list by hand. + +### Stale identifier/file naming + +Version numbers baked into names without a decimal point (`pre42`, `post_42`, files like `post-42-*.yml`) are invisible +to every dotted-version regex above, and a bare-digit content grep is unusably noisy (`42` also matches arbitrary +example values like `Int32: 42`). Search filenames and identifiers directly instead of file content: + +``` +find source -iname "*pre[0-9][0-9]*" -o -iname "*post[0-9][0-9]*" -o -iname "*pre-[0-9][0-9]*" -o -iname "*post-[0-9][0-9]*" +grep -rnE "def [a-zA-Z_]*(pre|post)_?[0-9]{2,}[a-zA-Z_]*\(" source --include="*.py" +``` + +The first finds filenames, the second finds generator function names. Both are zero-noise in this repo as of this +writing (they return exactly the known `post-42-*` cluster and its generator function, nothing else). Unlike the +patterns above, these are deliberately *not* scoped to the two versions in play — the whole point is to surface stale +names you don't already know about, and there are few enough to read by hand. Note the `{2,}` rather than `{2}`: an +undotted `10.0` is `100`, three digits, so a fixed two-digit quantifier would start missing names once the server +reaches double-digit majors. This is especially worth checking where a "pre-X" counterpart has already been removed, +since the surviving "post-X" name no longer makes sense on its own. + +### Removed commands/mechanisms + +These aren't discoverable by grep alone — you won't know their names in advance. Use `WebSearch` for MongoDB's official +"Compatibility Changes in MongoDB X.Y" release-note page for each major version between the old floor and the new floor +— don't assume a specific URL pattern will resolve directly (the release-notes site layout has changed and can +redirect); search for the page by title and follow the result. Each page enumerates exactly what was removed/changed in +that release. Confirm whether each removal landed *before* the new floor (in scope) or *at*/*after* it (out of scope, +same as anything at/above the floor), then grep the repo for the specific names you found. A "nothing removed at this +version is referenced in the repo" result is a legitimate, useful finding — don't treat an empty result as a failed +search. + +### Generated + `.template` file pairs + +Some YAML test files are generated, not hand-authored — check for a "Tests in this file are generated from +X.yml.template" comment at the top of the `.yml`, or a sibling file under `etc/templates/X.yml.template`. If you find +one, edit the `.template` source, not the generated `.yml` directly (a hand-edit to the generated file alone gets +silently overwritten the next time someone regenerates it), then regenerate. + +Regeneration is two separate steps, and `make -C source` only does the second one: + +1. **Template → `.yml`**: `make` does *not* process `.template` files. Each template family has its own generator + script, and the authoritative list of them is the "Regenerate JSON test files" step in + [`.github/workflows/unified-tests.yml`](../../../.github/workflows/unified-tests.yml) (e.g. + `python3 ./source/client-side-operations-timeout/etc/generate-basic-tests.py ./source/client-side-operations-timeout/etc/templates ./source/client-side-operations-timeout/tests`). + Run the script matching the template you edited, using that workflow's invocation verbatim rather than guessing + at arguments. That workflow is also what regenerates these files in CI, so anything it doesn't run won't be + regenerated automatically either. +2. **`.yml` → `.json`**: `make -C source` (this is all the Makefile does). + +If either step fails, fix the tooling (e.g. a Node/js-yaml version mismatch, or a missing `pymongo`/`pyyaml`/`jinja2`) — +don't hand-edit the generated files to match your template change instead. Hand-syncing risks drifting from what the +generator would actually produce, and that drift can go unnoticed indefinitely. If you can't get regeneration working, +don't make this edit; flag the blocked file to the operator instead of guessing. + +## Step 5: Verify Judgment Calls Against Ground Truth + +When a proposed simplification claims two conditions are now equivalent (e.g. "not standalone" now implies "supports +sessions" at the new floor), don't reason about it in the abstract — check a real driver implementation. Look for one +checked out as a sibling directory (e.g. `find ~ -maxdepth 3 -iname 'mongo-*-driver' -type d 2>/dev/null`) and grep its +source for the actual field/method involved (e.g. `logical_session_timeout_minutes`, `is_standalone`). If no driver +checkout is available, say so explicitly and flag the simplification as an unverified judgment call for human review +rather than guessing. This is cheap insurance: it caught a plausible-but-wrong simplification (a `change-streams.md` +edit that had to be reverted) before it shipped, and confirmed a reviewer's suggestion should be rejected rather than +accepted (a `retryable-writes.md` "redundant check" claim that turned out to be false — `logicalSessionTimeoutMinutes` +filters out arbiters/non-readable members, which `isStandalone()` alone does not). + +## Step 6: Batch, Checkpoint, Commit Incrementally + +Report findings grouped by confidence (fix / borderline) before editing anything. Apply fixes in small batches, validate +each batch with `pre-commit run --files ` immediately, and commit with descriptive messages — not one giant diff +at the end. This keeps review and rollback tractable across what is usually a multi-round, multi-day cleanup, and each +content-changing file needs its own `## Changelog` entry per this repo's convention. + +For numbered or named prose test lists that other drivers reference by index (e.g. `sessions/tests/README.md`, +`change-streams/tests/README.md`), never delete-and-renumber a dead entry — replace its content with `**Removed**` +instead, so the index stays stable. Plain (unordered) bullet lists have no such constraint and can be deleted outright. + +## Step 7: Expect Iteration + +Every time a new *shape* of miss turns up (e.g. spelled-out wire version vs. numeric, exact-floor qualifier vs. +below-floor, table cell vs. prose), immediately re-run a repo-wide sweep for that specific new pattern — don't just fix +the one instance found. Treat each new miss as evidence the pattern-net in Step 4 was incomplete, not as an isolated +bug. + +## Step 8: Watch for Tooling Side Effects + +- `mdformat` may auto-normalize changelog list spacing (loose vs. tight lists) — re-run `pre-commit` a second time after + the first "files were modified" failure to confirm it settles. +- Comment-only YAML edits don't affect generated JSON — but if `make -C source` is broken locally (e.g. a js-yaml/Node + version mismatch), verify this explicitly rather than assuming, and flag the tooling gap. + +## Delegating to Subagents + +Match the model to the task: + +- **Mechanical, well-specified batches** (e.g. "remove this exact header line from these N files") → cheap/fast model, + run in parallel. +- **Exhaustive repo-wide search-and-classify passes** → a stronger model, given the explicit fix/leave criteria from + Step 2 and instructed to read context around every hit, not just grep and report line matches. +- **Ground-truth verification against a reference driver implementation** → do directly; don't delegate a judgment call + you can't check yourself. + +## Closing Steps + +- Update `## Changelog` sections per file touched, following the repo's existing dating/ordering convention. +- Update the PR description and the JIRA ticket description to reflect the *actual final* scope — call out what was + found beyond the original estimate. Be careful not to mark the ticket "Resolved" while the PR is still awaiting + approvals; use "In Review" or similar until it's actually merged. +- If squashing commits before merge, verify the rewrite is lossless: confirm the new base is an ancestor of the original + tip (`git merge-base --is-ancestor `), then diff the squashed branch against the original tip to confirm + the resulting tree is identical before pushing. + +## Common Mistakes + +| Mistake | Fix | +| -------------------------------------------------------- | -------------------------------------------------------- | +| Scoping search to already-changed files | Always sweep the whole repo, every pass | +| Only searching for "below the floor" versions | Also search for "at the floor" qualifiers — equally dead | +| Classifying from the grep line alone | Read 10+ lines of context before deciding fix vs. leave | +| Assuming a simplification is equivalent without checking | Verify against a real driver implementation | +| Deleting numbered test entries other drivers reference | Mark `**Removed**`, keep the index stable | +| One giant edit at the end | Small batches, validated and committed incrementally | +| Fixing only the one instance of a newly-found miss shape | Re-sweep the whole repo for that pattern immediately | +| Marking a ticket/PR "Resolved" while still in review | Reflect actual state until merged | diff --git a/AGENTS.md b/AGENTS.md index 746766fea3..f9d9e944e6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -114,6 +114,9 @@ files instead of altering existing ones. - For spec changes that remove functionality: use `runOnRequirements` (unified tests) or have drivers skip the test (non-unified tests like SDAM). - Outdated prose tests must not be removed — mark them as such (e.g., strikethrough or *Removed*). +- Retiring an EOL server version (removing now-dead version-gated tests, prose, and pseudocode after the minimum + supported server version is raised) is a repo-wide sweep with many non-obvious traps. Use the + `retiring-server-versions` skill for the full checklist before starting one. ### Prose Test Numbering