Skip to content

fix(const,vendor,docs): audit registry case sensitivity, fold FEATCode.get (#903) - #913

Merged
JarryShaw merged 1 commit into
mainfrom
fix/903-registry-case-audit
Sep 29, 2026
Merged

JarryShaw merged 1 commit into
mainfrom
fix/903-registry-case-audit

Conversation

@JarryShaw

Copy link
Copy Markdown
Owner

What is the purpose of your pull request?

  • fix — corrects a defect
  • feat — adds a feature
  • perf — changes performance, not behaviour
  • refactor — changes neither behaviour nor performance
  • test — tests only
  • docs — documentation only
  • ci — workflows or build tooling
  • chore — anything else

Description of your pull request and other information

Closes #903 — the registry-wide case-sensitivity audit, under the owner's lenient criterion.

The audit table is the deliverable, and it lives in docs/source/conventions.rst beside the ruling, as the issue asks. Population, with the counting convention stated: 127 EnumRegistry subclasses across 124 files (117 int-valued, 10 StrEnum-valued), plus 24 non-registry enumerations by a runtime walk over both enum and aenum including nested classes — 151 in total. The 127 is 125 plus the two NGAP registries #880 landed while this was in flight.

The int tier needs no per-class row, and the table says why. A registry whose values are numbers has nothing for case to apply to; the only string that reaches get there is a member name, which Vendor.safe_name derives from the registrar's own column with casing preserved. Measured across all 151: exactly one would collide if names were folded (hip.Parameter, R1_Counter vs R1_COUNTER), and none has two str values that collide.

One defect found and fixed: FEATCode.get. It folded nothing, so it ran the case-sensitive default — but RFC 5797 §2, defining the very registry the crawler reads, states "IANA maintains uniqueness of feature names (FEAT codes) based on case-insensitive comparison." Both limbs hold: that comparison rule, and a measured spelling disagreement — RFC 2389 §3.2 recommends upper case on the wire while the registry spells 5 of its 15 codes lower case (of 64 CSV rows: 11 upper-case, 52 lower-case, 1 blank, 0 mixed). get('BASE') raised KeyError before. The fold is a fallback: an exact hit still delegates to the base, so precedence and the non-minting str path are untouched, and members keep the registrar's casing.

Two rows left open deliberately, both wider than a case fix and recorded in the table rather than acted on: a service-name lookup on the AppType transport registries (RFC 6335 §5.1 does make service names case-insensitive, but no such lookup exists to fold — new public API on a 6,000-member registry), and CommandType/ConformanceRequirement (the disagreement is measured, but nothing looks them up by string and neither inherits EnumLookup yet — #877 phase 2).

@JarryShaw JarryShaw added fix Pull requests that fix a defect (fix: subject prefix) const Regenerated IANA or vendor constant tables; members keep their numeric values test Pull requests that add or correct tests (test: subject prefix) review: pending No verdict for the current head - never reviewed, or the head moved since the last one labels Sep 29, 2026
@JarryShaw

Copy link
Copy Markdown
Owner Author

Verified the one behaviour change's justification myself, since the whole PR rests on it. The citation holds, with one wrinkle the table should mention.

RFC 5797 §2, lines 149–151 — quoted accurately, verbatim:

Future specifications are allowed to "upgrade" a placeholder to a true keyword unless it is specifically declared 'immutable' below, but otherwise IANA maintains uniqueness of feature names (FEAT codes) based on case-insensitive comparison.

Note for anyone re-checking this: grep -n 'case-insensitive' rfc5797.txt returns nothing, because the phrase wraps across the line break (case- / insensitive). My first pass read that as the claim failing. It isn't.

RFC 2389 §3.2 supports limb 2 as the table says — "implementations are recommended to use upper case letters when transmitting the feature response." But the same paragraph opens with:

The feature-label and feature-parms are nominally case sensitive, however the definitions of specific labels and parameters specify the precise interpretation, and it is to be expected that those definitions will usually specify the label and parameters in a case independent manner.

That does not overturn the verdict — RFC 5797 §2 is limb 1 and explicit, and §3.2's own next clause points the same way — but a contributor who follows the :rfc:2389#section-3.2`` link will hit "nominally case sensitive" first and think the row misread its source. Please add half a sentence to that cell noting §3.2 calls the label nominally case-sensitive and defers to each feature's own definition, which is what RFC 5797 then supplies.

Two other checks, both clean:

  • RFC 6335 §5.1 (lines 499–502) is quoted verbatim and is scoped to service names, not the Transport Protocol field. So the table is right to carry TransportProtocol on limb 2 and the TCP/UDP/SCTP/DCCP service-name row on limb 1; they are genuinely different rules in the same RFC.
  • FEATCode's 15 members hold no case-fold collision (15 distinct folded), and neither do CommandType (4) or ConformanceRequirement (3) in the same file. The fold is safe here — and semantically apt, since RFC 5797 uses case precisely to separate true keywords from pseudo-codes while forbidding a pair that differs only by case.

Cross-review is running on a different model from the author.

@JarryShaw

Copy link
Copy Markdown
Owner Author

GOOD TO GO on c0b88bbf5 — cross-review on sonnet (author opus). Two prose fixes applied by me since the verdict; the code is byte-identical to what was reviewed.

It re-derived the finding the whole audit rests on rather than accepting it: that the criterion is vacuous on the 117 int-valued registries, not merely unmet. It read Vendor.safe_name (pcapkit/vendor/default.py:239-267, no .upper()/.lower(), casing preserved verbatim) and traced every call site where wire data reaches an int-valued get — ftp.py:99-112 casts to int() first, ngap.py:452-461 passes pycrate's decoded ints. It also chased the one apparent counterexample, ngap.py's Criticality.get, which does take a string: Criticality is a plain IntEnum outside pcapkit/const, so it is not one of the 117, and the table already gives it separate and correct reasoning. The audit does not conflate the two.

Also independently confirmed: 127 registries / 124 files, 117 int (5 flags) + 10 str, 24 non-registry enumerations (17 top-level + 7 nested) = 151; exactly one name-fold collision (hip.Parameter's R1_Counter vs R1_COUNTER) and zero str-value collisions; the fold never mints, with both lookup tables still at 15 entries after exact, folded and missing probes in a fresh process; and VendorTemplateParityTests really does pin the generated get against the rendered template, so regeneration cannot drop it.

One discrepancy, which I re-derived myself: 7 test methods fail pre-change, not the 6 the commit message claimed. My list matches the reviewer's exactly. The cause is worth recording — reverting the three source files and running under plain unittest gives Ran 15 tests … failures=2, errors=36, because subTests inflate the outcome count, so the method tally has to come from distinct FAIL:/ERROR: names rather than the summary line. Amended to 7.

Also amended: the FEATCode cell now notes that :rfc:2389#section-3.2 opens by calling the feature-label "nominally case sensitive" before deferring to the specific definitions, and that the RFC 5797 §2 sentence wraps at case- / insensitive so a line grep misses it. Sphinx build after the edit: build succeeded, 58 warnings — the list-table cell is well-formed.

Deferrals all judged correct, including esp.py:282's _resolve, which the reviewer confirmed is a real case-insensitive fold living outside any get() — and found RFC 7296 states no case rule for it, so it needs its own decision rather than a copy of this one.

Unverified and stated as such: the exact coverage percentages, and mypy/pylint/isort were not re-run.

@JarryShaw JarryShaw added review: good-to-go Cross-review at the current head says ready; CI state is separate and removed review: pending No verdict for the current head - never reviewed, or the head moved since the last one labels Sep 29, 2026
@JarryShaw

Copy link
Copy Markdown
Owner Author

Resolve conflicts. And I'm thinking we should probably consider splitting conventions.rst into several files per category as it grows longer.

…e.get (#903)

- Record the audit in docs/source/conventions.rst: the lenient criterion's two
  limbs, the population it covers (127 registries, 24 helper enumerations), and
  a per-class table of governing source, quoted rule and verdict.
- FEATCode.get was case-sensitive with nothing behind it, and RFC 5797 Section 2
  states the opposite outright -- IANA keeps FEAT codes unique by
  case-insensitive comparison -- so give it a folding get, in the crawler
  template as well as the generated module.
- The fold is a fallback only: an exact name or value hit still delegates to the
  base, so name-before-value precedence and the non-minting str path are
  unchanged, and nothing calls cls(key).
- Correct three stale prose claims this invalidates: the "Four do" override
  count on the conventions page, and two test-module notes stating FEATCode
  carries no get of its own.
- Remeasure "the 125 classes that reach this method" in pcapkit/corekit/enum.py
  as 127, after #880's two NGAP registries landed.

15 new tests, 7 of which fail without the fix (measured by reverting the three
source files and re-running under plain unittest, since pytest-subtests
miscounts a method whose subTests fail). tests/const and tests/corekit
pass: 549 passed, 16 skipped, 40970 subtests. mypy and pylint show no new
finding; isort clean.
@JarryShaw
JarryShaw force-pushed the fix/903-registry-case-audit branch from c0b88bb to 50813f1 Compare September 29, 2026 13:08
@JarryShaw

Copy link
Copy Markdown
Owner Author

Rebased onto main after #912 merged (9806f16aa) — 50813f1df. The conflict was not a content clash: #912 moved docs/source/conventions.rst to docs/source/contributing/conventions.rst, and GitHub scores an edit against a deleted path as DIRTY even where git's rename detection resolves it. Cherry-picked with -X find-renames, so the audit section landed on the moved file and the old path was not recreated.

Verified blob-identical to the head you reviewed, file by file — git rev-parse on all seven paths gives the same object for c0b88bbf5 and 50813f1df, including conventions.rst across the rename. So nothing changed but where one file lives, and the review: good-to-go verdict carries rather than needing a re-review. tests/const/test_const_ftp_featcode_case_903_unit.py still passes 15/15 on the rebased tree.

The four pre-existing test references to the doc already point at contributing/conventions.rst — #912 updated those, so no fixup was needed here.

On splitting conventions.rst: agreed, and it is getting hard to argue against. Verbatim:

I'm thinking we should probably consider splitting conventions.rst into several files per category as it grows longer.

It is now ~440 lines carrying three unrelated rulings — the mint/unmint criterion, sentinel naming, and the registry protocol plus this case-sensitivity audit. A reader wanting the sentinel rule reads past two hundred lines about enum minting to reach it.

I have not folded that into this PR, because it would turn a reviewed and verified change into a reviewed change plus an unreviewed restructure, and the doc move has already cost three rebases today. I will file it as its own issue once this lands, and the natural split is one file per .. _label: anchor already in the file — mint-criterion, sentinel-convention, registry-protocol — under docs/source/contributing/conventions/, with the parent becoming a toctree. Say if you would rather have a different cut.

@JarryShaw
JarryShaw merged commit 1f4337e into main Sep 29, 2026
63 checks passed
@JarryShaw
JarryShaw deleted the fix/903-registry-case-audit branch September 29, 2026 13:54
@JarryShaw JarryShaw removed the review: good-to-go Cross-review at the current head says ready; CI state is separate label Sep 29, 2026
JarryShaw added a commit that referenced this pull request Sep 29, 2026
…up (#877)

Phase 2 of #877, the 11 classes across 8 files not held by #913/#904:
TransportProtocol, FinalisedState, Completion, ftp.Type, httpv1.Type,
Criticality, PDUKind, PacketDirection, PacketReception, WireGuardKeyLabel,
and FrameType.Flags (carrying its 6 per-frame subclasses transitively).
Each now mixes in EnumLookup ahead of its enum base for the shared
get/get_all contract.

TransportProtocol and Criticality already had their own get, both as a
staticmethod against EnumLookup.get's classmethod (the #908/#915 trap).
Both are now classmethods delegating to super().get(), keeping only what
the base does not reproduce -- TransportProtocol's case-fold and no-mint
refusal, Criticality's case-sensitive miss -- each re-raised as the
ValueError callers already depend on rather than the base's KeyError.
Each gained a default parameter forwarded to the base, since dropping one
the base declares is a real classmethod-override violation under mypy.

Updated test_const_enum_get.py's exclusion set for TransportProtocol's
new default, and added test_enum_lookup_reparent_877_unit.py pinning the
re-parenting, both preserved overrides, and no member-table growth.
JarryShaw added a commit that referenced this pull request Sep 29, 2026
…up (#877)

Phase 2 of #877, the 11 classes across 8 files not held by #913/#904:
TransportProtocol, FinalisedState, Completion, ftp.Type, httpv1.Type,
Criticality, PDUKind, PacketDirection, PacketReception, WireGuardKeyLabel,
and FrameType.Flags (carrying its 6 per-frame subclasses transitively).
Each now mixes in EnumLookup ahead of its enum base for the shared
get/get_all contract.

TransportProtocol and Criticality already had their own get, both as a
staticmethod against EnumLookup.get's classmethod (the #908/#915 trap).
Both are now classmethods delegating to super().get(), keeping only what
the base does not reproduce -- TransportProtocol's case-fold and no-mint
refusal, Criticality's case-sensitive miss -- each re-raised as the
ValueError callers already depend on rather than the base's KeyError.
Each gained a default parameter forwarded to the base, since dropping one
the base declares is a real classmethod-override violation under mypy.

Updated test_const_enum_get.py's exclusion set for TransportProtocol's
new default, and added test_enum_lookup_reparent_877_unit.py pinning the
re-parenting, both preserved overrides, and no member-table growth.
JarryShaw added a commit that referenced this pull request Sep 29, 2026
…up (#877) (#921)

Phase 2 of #877, the 11 classes across 8 files not held by #913/#904:
TransportProtocol, FinalisedState, Completion, ftp.Type, httpv1.Type,
Criticality, PDUKind, PacketDirection, PacketReception, WireGuardKeyLabel,
and FrameType.Flags (carrying its 6 per-frame subclasses transitively).
Each now mixes in EnumLookup ahead of its enum base for the shared
get/get_all contract.

TransportProtocol and Criticality already had their own get, both as a
staticmethod against EnumLookup.get's classmethod (the #908/#915 trap).
Both are now classmethods delegating to super().get(), keeping only what
the base does not reproduce -- TransportProtocol's case-fold and no-mint
refusal, Criticality's case-sensitive miss -- each re-raised as the
ValueError callers already depend on rather than the base's KeyError.
Each gained a default parameter forwarded to the base, since dropping one
the base declares is a real classmethod-override violation under mypy.

Updated test_const_enum_get.py's exclusion set for TransportProtocol's
new default, and added test_enum_lookup_reparent_877_unit.py pinning the
re-parenting, both preserved overrides, and no member-table growth.
JarryShaw added a commit that referenced this pull request Sep 29, 2026
… onto EnumLookup (#930)

Finishes #877's phase 2, which #921 left seven classes out of because their
files were held by #913/#904 at the time: CommandType and
ConformanceRequirement (const/ftp/command.py), ESPStatus
(protocols/internet/esp.py), and FastBindingAcknowledgmentStatus,
IPv6AddressPrefixCode, LMAAddressCode and LocalizedRoutingStatus
(protocols/internet/mh.py). Both blockers have since merged. Each now mixes
in EnumLookup ahead of its enum base; member-table sizes are unchanged.

FastBindingAcknowledgmentStatus and IPv6AddressPrefixCode kept their own
get, still a staticmethod that never calls super() -- left untouched, since
#923's EnumKeyError name-miss conversion already matches the base's shape.
mypy's [override] and pylint's arguments-differ against the kept decorator
are suppressed rather than resolved by widening it. The other five are
pure re-parents.

Brought conventions.rst and its own doc-claims test in line with #929,
which merged in the interim: phase 2 is now 24 of 24, zero enumerations
outside the hierarchy. Added test_enum_lookup_reparent_930_unit.py pinning
the re-parenting, the kept overrides, no growth, and the zero-outside
census; fixed three tests whose claims this change made stale
(test_const_enum_get, test_const_ftp_featcode_case_903_unit, test_mh_unit).

Build: mypy/pylint/isort clean against baseline; affected test files pass.
JarryShaw added a commit that referenced this pull request Sep 29, 2026
… onto EnumLookup (#930)

Finishes #877's phase 2, which #921 left seven classes out of because their
files were held by #913/#904 at the time: CommandType and
ConformanceRequirement (const/ftp/command.py), ESPStatus
(protocols/internet/esp.py), and FastBindingAcknowledgmentStatus,
IPv6AddressPrefixCode, LMAAddressCode and LocalizedRoutingStatus
(protocols/internet/mh.py). Both blockers have since merged. Each now mixes
in EnumLookup ahead of its enum base; member-table sizes are unchanged.

FastBindingAcknowledgmentStatus and IPv6AddressPrefixCode kept their own
get, still a staticmethod that never calls super() -- left untouched, since
#923's EnumKeyError name-miss conversion already matches the base's shape.
mypy's [override] and pylint's arguments-differ against the kept decorator
are suppressed rather than resolved by widening it. The other five are
pure re-parents.

Brought conventions.rst and its own doc-claims test in line with #929,
which merged in the interim: phase 2 is now 24 of 24, zero enumerations
outside the hierarchy. Added test_enum_lookup_reparent_930_unit.py pinning
the re-parenting, the kept overrides, no growth, and the zero-outside
census; fixed three tests whose claims this change made stale
(test_const_enum_get, test_const_ftp_featcode_case_903_unit, test_mh_unit).

Build: mypy/pylint/isort clean against baseline; affected test files pass.
JarryShaw added a commit that referenced this pull request Sep 29, 2026
… onto EnumLookup (#930)

Finishes #877's phase 2, which #921 left seven classes out of because their
files were held by #913/#904 at the time: CommandType and
ConformanceRequirement (const/ftp/command.py), ESPStatus
(protocols/internet/esp.py), and FastBindingAcknowledgmentStatus,
IPv6AddressPrefixCode, LMAAddressCode and LocalizedRoutingStatus
(protocols/internet/mh.py). Both blockers have since merged. Each now mixes
in EnumLookup ahead of its enum base; member-table sizes are unchanged.

FastBindingAcknowledgmentStatus and IPv6AddressPrefixCode kept their own
get, still a staticmethod that never calls super() -- left untouched, since
#923's EnumKeyError name-miss conversion already matches the base's shape.
mypy's [override] and pylint's arguments-differ against the kept decorator
are suppressed rather than resolved by widening it. The other five are
pure re-parents.

Brought conventions.rst and its own doc-claims test in line with #929,
which merged in the interim: phase 2 is now 24 of 24, zero enumerations
outside the hierarchy. Added test_enum_lookup_reparent_930_unit.py pinning
the re-parenting, the kept overrides, no growth, and the zero-outside
census; fixed three tests whose claims this change made stale
(test_const_enum_get, test_const_ftp_featcode_case_903_unit, test_mh_unit).

Build: mypy/pylint/isort clean against baseline; affected test files pass.
JarryShaw added a commit that referenced this pull request Sep 30, 2026
… onto EnumLookup (#932)

Finishes #877's phase 2, which #921 left seven classes out of because their
files were held by #913/#904 at the time: CommandType and
ConformanceRequirement (const/ftp/command.py), ESPStatus
(protocols/internet/esp.py), and FastBindingAcknowledgmentStatus,
IPv6AddressPrefixCode, LMAAddressCode and LocalizedRoutingStatus
(protocols/internet/mh.py). Both blockers have since merged. Each now mixes
in EnumLookup ahead of its enum base; member-table sizes are unchanged.

FastBindingAcknowledgmentStatus and IPv6AddressPrefixCode kept their own
get, still a staticmethod that never calls super() -- left untouched, since
#923's EnumKeyError name-miss conversion already matches the base's shape.
mypy's [override] and pylint's arguments-differ against the kept decorator
are suppressed rather than resolved by widening it. The other five are
pure re-parents.

Brought conventions.rst and its own doc-claims test in line with #929,
which merged in the interim: phase 2 is now 24 of 24, zero enumerations
outside the hierarchy. Added test_enum_lookup_reparent_930_unit.py pinning
the re-parenting, the kept overrides, no growth, and the zero-outside
census; fixed three tests whose claims this change made stale
(test_const_enum_get, test_const_ftp_featcode_case_903_unit, test_mh_unit).

Build: mypy/pylint/isort clean against baseline; affected test files pass.
JarryShaw added a commit that referenced this pull request Sep 30, 2026
RFC 959's rendered HTML carries anchors only for its eight top-level sections,
``section-1`` through ``section-8``, so **every** sub-numbered ``959`` fragment
is a live link to nothing. Per the ruling on #944, they now cite the enclosing
top-level section.

- ``#section-4.1`` -> ``#section-4`` at eleven sites: the six in
  ``pcapkit/{const,vendor}/{ftp/command,http/method}.py`` #944 reported, plus
  four rendered roles in
  ``docs/source/contributing/conventions/registry-protocol.rst`` and one in
  ``tests/const/test_const_enum_no_mint.py``. The five extra sites predate this
  branch -- ``git log -S`` puts them at #913 (``1f4337e12``) -- and were found
  by the cross-review, not by #944's ``pcapkit/``-only grep.
- ``#section-5.3`` -> ``#section-5`` at two more, in
  ``tests/protocols/application/test_ftp_unit.py`` and
  ``docs/source/changelog/1.5.0.rst``, cited for the case-insensitivity rule
  §5.3 does correctly name in prose. ``CHANGELOG.md`` regenerated with
  ``util/changelog_md.py`` for the second of those, one line.
- The three ``pcapkit/`` template/generated pairs stay byte-identical on the
  cited lines; each string is a literal in the ``vendor/`` template, verified to
  contain no interpolation, so a regeneration reproduces the edit. No crawl was
  run.
- Add ``KNOWN_DEAD_ANCHORS`` (both ``959`` fragments) and
  ``test_no_known_dead_anchor_citations`` to
  ``tests/project/test_rfc_anchor_fragments.py``. #943's shape invariant accepts
  ``section-4.1`` as well-formed and by construction cannot see a dead anchor,
  so this is a narrower second check for the same class. It scans ``pcapkit/``,
  ``docs/source`` and ``tests/`` rather than ``pcapkit/`` alone -- looking at one
  directory is what let five sites sit unnoticed -- and matches roles only, via a
  lookbehind, so a ``literal`` naming the defect stays writable. Offline by
  design: CI has no network.

Prose left standing where it is still true: ``ftp/command.py``'s citations carry
the command-*kind* claim, which §4 does support. ``http/method.py``'s carry a
case-insensitivity claim that §4 does not; that is #947's question, deliberately
not widened into here.

tests/project: 207 passed, 1 skipped, 562 subtests.
tests/protocols/application/test_ftp_unit.py + tests/const/test_const_enum_no_mint.py:
79 passed, 602 subtests. Each new assertion was shown to fail without its fix,
including from a ``docs/`` site the pre-widening scan could not see.
JarryShaw added a commit that referenced this pull request Sep 30, 2026
RFC 959's rendered HTML carries anchors only for its eight top-level sections,
``section-1`` through ``section-8``, so **every** sub-numbered ``959`` fragment
is a live link to nothing. Per the rulings on #944 and #947, they now cite the
section that supports the claim they carry.

- ``#section-4.1`` -> ``#section-4`` at eleven sites: the six in
  ``pcapkit/{const,vendor}/{ftp/command,http/method}.py`` #944 reported, plus
  four rendered roles in
  ``docs/source/contributing/conventions/registry-protocol.rst`` and one in
  ``tests/const/test_const_enum_no_mint.py``. The five extra sites predate this
  branch -- ``git log -S`` puts them at #913 (``1f4337e12``) -- and were found
  by cross-review, not by #944's ``pcapkit/``-only grep.
- ``#section-5.3`` -> ``#section-5`` at two more, in
  ``tests/protocols/application/test_ftp_unit.py`` and
  ``docs/source/changelog/1.5.0.rst``. ``CHANGELOG.md`` regenerated with
  ``util/changelog_md.py`` for the second, one line.
- The three ``pcapkit/`` template/generated pairs stay byte-identical on the
  cited lines; each string is a literal in the ``vendor/`` template, carrying no
  interpolation, so a regeneration reproduces the edit. No crawl was run.
- Add ``KNOWN_DEAD_ANCHORS`` (both ``959`` fragments) and
  ``test_no_known_dead_anchor_citations`` to
  ``tests/project/test_rfc_anchor_fragments.py``. #943's shape invariant accepts
  ``section-4.1`` as well-formed and by construction cannot see a dead anchor,
  so this is a narrower second check for the same class. It scans ``pcapkit/``,
  ``docs/source`` and ``tests/`` rather than ``pcapkit/`` alone -- looking at one
  directory is what let five sites sit unnoticed. Offline by design: CI has no
  network.
- Classify role-versus-literal by **masking inline literal spans** before
  matching, rather than by a one-character lookbehind. The lookbehind was wrong
  in both directions, measured: a role written straight after a closing literal
  or a title reference was silently skipped, and an inert literal padded with
  spaces was wrongly flagged. Masking fixes all three. Two residual gaps are
  documented rather than claimed away -- a ``::`` literal block, and a
  triple-backtick wrapper around a padded literal -- both of which fail in the
  safe direction, since a false positive fails loudly where a false negative is
  a dead link nobody sees.

Prose left standing where it is still true: ``ftp/command.py``'s citations carry
the command-*kind* claim, which §4 does support. ``http/method.py``'s carry a
case-insensitivity claim stated in §5.3, tracked in #947.

Dead sub-anchors in *other* RFCs -- ten sites across RFC 1122 and RFC 719, both
of which render no sub-section anchors either -- are out of #944's scope and are
filed as #950.

tests/project: 207 passed, 1 skipped, 562 subtests.
tests/protocols/application/test_ftp_unit.py + tests/const/test_const_enum_no_mint.py:
79 passed, 602 subtests. Each new assertion was shown to fail without its fix,
including from a ``docs/`` site the pre-widening scan could not see.
JarryShaw added a commit that referenced this pull request Sep 30, 2026
RFC 959's rendered HTML carries anchors only for its eight top-level sections,
``section-1`` through ``section-8``, so **every** sub-numbered ``959`` fragment
is a live link to nothing. Per the rulings on #944 and #947, they now cite the
section that supports the claim they carry.

- ``#section-4.1`` -> ``#section-4`` at eleven sites: the six in
  ``pcapkit/{const,vendor}/{ftp/command,http/method}.py`` #944 reported, plus
  four rendered roles in
  ``docs/source/contributing/conventions/registry-protocol.rst`` and one in
  ``tests/const/test_const_enum_no_mint.py``. The five extra sites predate this
  branch -- ``git log -S`` puts them at #913 (``1f4337e12``) -- and were found
  by cross-review, not by #944's ``pcapkit/``-only grep.
- ``#section-5.3`` -> ``#section-5`` at two more, in
  ``tests/protocols/application/test_ftp_unit.py`` and
  ``docs/source/changelog/1.5.0.rst``. ``CHANGELOG.md`` regenerated with
  ``util/changelog_md.py`` for the second, one line.
- The three ``pcapkit/`` template/generated pairs stay byte-identical on the
  cited lines; each string is a literal in the ``vendor/`` template, carrying no
  interpolation, so a regeneration reproduces the edit. No crawl was run.
- Add ``KNOWN_DEAD_ANCHORS`` (both ``959`` fragments) and
  ``test_no_known_dead_anchor_citations`` to
  ``tests/project/test_rfc_anchor_fragments.py``. #943's shape invariant accepts
  ``section-4.1`` as well-formed and by construction cannot see a dead anchor,
  so this is a narrower second check for the same class. It scans ``pcapkit/``,
  ``docs/source`` and ``tests/`` rather than ``pcapkit/`` alone -- looking at one
  directory is what let five sites sit unnoticed. Offline by design: CI has no
  network.
- Classify role-versus-literal by **masking inline literal spans** before
  matching, rather than by a one-character lookbehind. The lookbehind was wrong
  in both directions, measured: a role written straight after a closing literal
  or a title reference was silently skipped, and an inert literal padded with
  spaces was wrongly flagged. Masking fixes all three. Two residual gaps are
  documented rather than claimed away -- a ``::`` literal block, and a
  triple-backtick wrapper around a padded literal -- both of which fail in the
  safe direction, since a false positive fails loudly where a false negative is
  a dead link nobody sees.

Prose left standing where it is still true: ``ftp/command.py``'s citations carry
the command-*kind* claim, which §4 does support. ``http/method.py``'s carry a
case-insensitivity claim stated in §5.3, tracked in #947.

Dead sub-anchors in *other* RFCs -- ten sites across RFC 1122 and RFC 719, both
of which render no sub-section anchors either -- are out of #944's scope and are
filed as #950.

tests/project: 207 passed, 1 skipped, 562 subtests.
tests/protocols/application/test_ftp_unit.py + tests/const/test_const_enum_no_mint.py:
79 passed, 602 subtests. Each new assertion was shown to fail without its fix,
including from a ``docs/`` site the pre-widening scan could not see.
JarryShaw added a commit that referenced this pull request Sep 30, 2026
RFC 959's rendered HTML carries anchors only for its eight top-level sections,
``section-1`` through ``section-8``, so **every** sub-numbered ``959`` fragment
is a live link to nothing. Per the rulings on #944 and #947, they now cite the
section that supports the claim they carry.

- ``#section-4.1`` -> ``#section-4`` at eleven sites: the six in
  ``pcapkit/{const,vendor}/{ftp/command,http/method}.py`` #944 reported, plus
  four rendered roles in
  ``docs/source/contributing/conventions/registry-protocol.rst`` and one in
  ``tests/const/test_const_enum_no_mint.py``. The five extra sites predate this
  branch -- ``git log -S`` puts them at #913 (``1f4337e12``) -- and were found
  by cross-review, not by #944's ``pcapkit/``-only grep.
- ``#section-5.3`` -> ``#section-5`` at two more, in
  ``tests/protocols/application/test_ftp_unit.py`` and
  ``docs/source/changelog/1.5.0.rst``. ``CHANGELOG.md`` regenerated with
  ``util/changelog_md.py`` for the second, one line.
- The three ``pcapkit/`` template/generated pairs stay byte-identical on the
  cited lines; each string is a literal in the ``vendor/`` template, carrying no
  interpolation, so a regeneration reproduces the edit. No crawl was run.
- Add ``KNOWN_DEAD_ANCHORS`` (both ``959`` fragments) and
  ``test_no_known_dead_anchor_citations`` to
  ``tests/project/test_rfc_anchor_fragments.py``. #943's shape invariant accepts
  ``section-4.1`` as well-formed and by construction cannot see a dead anchor,
  so this is a narrower second check for the same class. It scans ``pcapkit/``,
  ``docs/source`` and ``tests/`` rather than ``pcapkit/`` alone -- looking at one
  directory is what let five sites sit unnoticed. Offline by design: CI has no
  network.
- Classify role-versus-literal by **masking inline literal spans** before
  matching, rather than by a one-character lookbehind. The lookbehind was wrong
  in both directions, measured: a role written straight after a closing literal
  or a title reference was silently skipped, and an inert literal padded with
  spaces was wrongly flagged. Masking fixes all three. Two residual gaps are
  documented rather than claimed away -- a ``::`` literal block, and a
  triple-backtick wrapper around a padded literal -- both of which fail in the
  safe direction, since a false positive fails loudly where a false negative is
  a dead link nobody sees.

Prose left standing where it is still true: ``ftp/command.py``'s citations carry
the command-*kind* claim, which §4 does support. ``http/method.py``'s carry a
case-insensitivity claim stated in §5.3, tracked in #947.

Dead sub-anchors in *other* RFCs -- ten sites across RFC 1122 and RFC 719, both
of which render no sub-section anchors either -- are out of #944's scope and are
filed as #950.

tests/project: 207 passed, 1 skipped, 562 subtests.
tests/protocols/application/test_ftp_unit.py + tests/const/test_const_enum_no_mint.py:
79 passed, 602 subtests. Each new assertion was shown to fail without its fix,
including from a ``docs/`` site the pre-widening scan could not see.
JarryShaw added a commit that referenced this pull request Sep 30, 2026
RFC 959's rendered HTML carries anchors only for its eight top-level sections,
``section-1`` through ``section-8``, so **every** sub-numbered ``959`` fragment
is a live link to nothing. Per the rulings on #944 and #947, they now cite the
section that supports the claim they carry.

- ``#section-4.1`` -> ``#section-4`` at eleven sites: the six in
  ``pcapkit/{const,vendor}/{ftp/command,http/method}.py`` #944 reported, plus
  four rendered roles in
  ``docs/source/contributing/conventions/registry-protocol.rst`` and one in
  ``tests/const/test_const_enum_no_mint.py``. The five extra sites predate this
  branch -- ``git log -S`` puts them at #913 (``1f4337e12``) -- and were found
  by cross-review, not by #944's ``pcapkit/``-only grep.
- ``#section-5.3`` -> ``#section-5`` at two more, in
  ``tests/protocols/application/test_ftp_unit.py`` and
  ``docs/source/changelog/1.5.0.rst``. ``CHANGELOG.md`` regenerated with
  ``util/changelog_md.py`` for the second, one line.
- The three ``pcapkit/`` template/generated pairs stay byte-identical on the
  cited lines; each string is a literal in the ``vendor/`` template, carrying no
  interpolation, so a regeneration reproduces the edit. No crawl was run.
- Add ``KNOWN_DEAD_ANCHORS`` (both ``959`` fragments) and
  ``test_no_known_dead_anchor_citations`` to
  ``tests/project/test_rfc_anchor_fragments.py``. #943's shape invariant accepts
  ``section-4.1`` as well-formed and by construction cannot see a dead anchor,
  so this is a narrower second check for the same class. It scans ``pcapkit/``,
  ``docs/source`` and ``tests/`` rather than ``pcapkit/`` alone -- looking at one
  directory is what let five sites sit unnoticed. Offline by design: CI has no
  network.
- Classify role-versus-literal by **masking inline literal spans** before
  matching, rather than by a one-character lookbehind. The lookbehind was wrong
  in both directions, measured: a role written straight after a closing literal
  or a title reference was silently skipped, and an inert literal padded with
  spaces was wrongly flagged. Masking fixes all three. Two residual gaps are
  documented rather than claimed away -- a ``::`` literal block, and a
  triple-backtick wrapper around a padded literal -- both of which fail in the
  safe direction, since a false positive fails loudly where a false negative is
  a dead link nobody sees.

Prose left standing where it is still true: ``ftp/command.py``'s citations carry
the command-*kind* claim, which §4 does support. ``http/method.py``'s carry a
case-insensitivity claim stated in §5.3, tracked in #947.

Dead sub-anchors in *other* RFCs -- ten sites across RFC 1122 and RFC 719, both
of which render no sub-section anchors either -- are out of #944's scope and are
filed as #950.

tests/project: 207 passed, 1 skipped, 562 subtests.
tests/protocols/application/test_ftp_unit.py + tests/const/test_const_enum_no_mint.py:
79 passed, 602 subtests. Each new assertion was shown to fail without its fix,
including from a ``docs/`` site the pre-widening scan could not see.
JarryShaw added a commit that referenced this pull request Oct 1, 2026
…ision (#719)

Accuracy fixes, each re-derived against the code:

- mint-criterion.rst presented `EtherType`'s company names and `Socket`'s
  `Registered by Xerox` as the worked *mint* examples. #878 converted both to
  `_unregistered_member`; `CGAType` is now the only `_missing_` that mints, so
  the criterion decides the unregistered member's *name*, not whether it
  registers.
- Its `ast` snippet matched only `ast.Name` callees, so it reported 1 MINT and
  0 UNMINT; `_unregistered_member` is called on `cls`. Matches attributes now.
- "Every registry defines `_missing_`" -> 121 of 127, naming the six without one.
- registry-protocol.rst: `__new__` exemption said "a handful ... tracked in
  #860"; it is six named classes and #860 closed with all 127 on the base.
- The 6 mh/ngap helpers are not all numeric: `PDUKind` is `str`, and it was
  listed in two rows at once.
- `TCP`/`UDP`/`SCTP`/`DCCP` member *names* come from the service-name column;
  the values are composites.
- process.rst: 9 sections, 8 of them module-level; `pcapkit.interface` has none.
- sentinel-convention.rst: `NO_VALUE` also lacks `__copy__`/`__deepcopy__`/
  `__reduce__`; the quoted `AbsentType` excerpt did not support the privacy
  claim it was cited for.
- Two `/issues/` links pointed at pull requests (#847, #913).

Concision: dropped timed context (the page's former title, the two-pass #877
history, the pre-#937 casing narrative, the pre-restructure changelog shape) and
fixed a duplicated clause. Added two Mermaid flows for `_missing_` and for
`get`'s dispatch, modelled on workflows.rst:102.

Build: docutils parse unchanged from base; tests/project/test_conventions_doc_claims.py
and the seven other suites reading these pages 111 passed, 1 skipped, 241 subtests.
@JarryShaw JarryShaw added this to the 1.5 milestone Oct 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

const Regenerated IANA or vendor constant tables; members keep their numeric values fix Pull requests that fix a defect (fix: subject prefix) test Pull requests that add or correct tests (test: subject prefix)

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

test(const): audit every registry case sensitivity against its source specification

1 participant