Skip to content

refactor(ipv6): rename IPv6_GenericExt to IPv6_Ext and make it the shared base (#917) - #924

Merged
JarryShaw merged 1 commit into
mainfrom
refactor/917-ipv6-ext-base
Sep 29, 2026
Merged

JarryShaw merged 1 commit into
mainfrom
refactor/917-ipv6-ext-base

Conversation

@JarryShaw

Copy link
Copy Markdown
Owner

Please follow the guide below

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 #917. IPv6_GenericExt becomes IPv6_Ext and serves both roles the owner
asked for: the shared base of all 8 IPv6 extension headers, and the RFC 6564 fallback
parser. Generic in _PT/_ST like IPsec, so AH(IPsec, IPv6_Ext) and
ESP(IPsec, IPv6_Ext) carry two identically-parameterised generic bases — both
linearise, and mypy and pylint both accept them (#891 had left that unverified).

The re-parent is not free, and three shadowing traps were measured rather than
assumed.
A concrete base shadows anything a subclass does not override:

  • HOPOPT, MH, AH and ESP never declared alias — they took
    ProtocolBase's class-name default. They would have inherited 'IPv6-Ext',
    renaming each in every ProtoChain string and packet-dict key. Spelled out now,
    at exactly the old values. (The issue's premise that all eight already shadow
    name and alias holds only for name.)
  • All eight write protocol as return super().protocol, which now lands on the
    base — whose protocol is repointed at the fallback's own ExtensionHeader
    identity. Measured: AttributeError on all eight. The base discriminates on
    __data__ (not isinstance(self._info, …), which breaks a make-only instance).
  • The base's version == 6 gate would reject AH/ESP, which default to
    version=4: ProtocolError: ESP: only valid for IPv6, got version=4. Scoped to
    the fallback role too.

#895 is discharged. ESP inherits the _extf guards on payload/protochain
and gets protocol by hand, so _extf — assigned and never read before — is now
load-bearing. Its read still has no if extension: return short-circuit; that
saves work rather than changing behaviour, and is not part of this change.

Verified: all 8 keep their protochain segment, payload/protocol/protochain
guards and length; codes 147/253/254 still give protochain='IPv6:Raw:Raw' with
src/dst intact (#904's point); next is a new shared member, previously
AttributeError on all eight. 331 passed / 1420 subtests (main: 317 / 1346), zero
failures. ipv6_ext.py coverage 87% → 100%. mypy 321 errors — identical to
main, every category; pylint 627 messages against main's 629, no new errors.

The new test class walks IPv6_Ext.__subclasses__() at runtime and requires each
subclass to define name, alias, protocol, length and __index__ itself
(checked against __dict__, since getattr cannot tell an override from an
inherited fallback answer). Deleting HOPOPT.name fails it:

AssertionError: False is not true : HOPOPT inherits 'name' from IPv6_Ext, whose value
is the generic fallback parser's and is wrong for a header with an identity of its own;
define it on HOPOPT itself

One deliberate deviation from the issue: the alias assertion is "no underscore, and
alias.lstrip('IPv6-').lower() is a usable key" rather than "is hyphenated", because
HOPOPT/MH/AH/ESP have no IPv6 prefix for lstrip to bite on — requiring a
hyphen of them would demand a rename, not test an invariant.

@JarryShaw JarryShaw added refactor Restructuring for its own sake — neither a fix nor a new capability (refactor: prefix) breaking Breaks public-facing behaviour or API (apply alongside the type label) test Pull requests that add or correct tests (test: subject prefix) docs Pull requests that change documentation only (docs: 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

GOOD TO GO on the code, from a Sonnet cross-review (the author was Opus). But one thing needs your call before merge, so I have labelled this needs: decision.

The rename leaves no backward-compatibility alias, and the break is complete. Measured by me on both trees, printing pcapkit.__file__ each side:

main  ada85bbfc:  from pcapkit import IPv6_GenericExt                     -> OK
                  from pcapkit.protocols.internet import IPv6_GenericExt  -> OK
                  from pcapkit import IPv6_Ext                            -> AttributeError
branch 956fb9afe: from pcapkit import IPv6_GenericExt                     -> AttributeError
                  from pcapkit.protocols.internet import IPv6_GenericExt  -> AttributeError
                  from pcapkit import IPv6_Ext                            -> OK

Both pcapkit/__init__.py and pcapkit/protocols/internet/__init__.py drop the old name from their imports and __all__ with no replacement. Any third-party code importing IPv6_GenericExt, and any pickle naming pcapkit.protocols.internet.ipv6_generic_ext.IPv6_GenericExt, breaks with no migration path.

The question: ship it bare, or leave a deprecated alias? The reviewer found no alias-on-rename convention anywhere under pcapkit/protocols/, so bare appears to be house practice, and this PR is labelled breaking against an issue you approved with "Go" — which is why it is a needs: decision rather than a NEEDS CHANGES. It is still a hard break in a public name, so I would rather you said it than have me assume it.

Everything else confirmed, independently derived rather than taken from the PR:

  • Rename is complete — zero hits for IPv6_GenericExt, ipv6_generic_ext, Data_IPv6_GenericExt, Schema_IPv6_GenericExt across pcapkit/, tests/, docs/, examples/, util/. Checked by grep, not by the docs build, which stays green on a dead cross-reference.
  • All 8 headers inherit the base at runtime. The four larger diffs are explained: ah.py/hopopt.py/mh.py each add a spelled-out alias because the new concrete base would otherwise shadow the class-name default with 'IPv6-Ext'; esp.py double-inherits IPsec + IPv6_Ext and gains alias plus a protocol property with an _extf guard — and _extf was assigned but never read anywhere before, so it was dead until now.
  • __index__ contract holds — the base raises UnsupportedCall, and all eight define their own returning the real IANA number: HOPOPT 0, IPv6_Route 43, IPv6_Frag 44, ESP 50, AH 51, IPv6_Opts 60, MH 135, HIP 139.
  • No test coverage lost in the 368-line → 725-line replacement: all 15 old method names survive verbatim, the body diff is cosmetic re-indentation plus 'IPv6-GenericExt'→'IPv6-Ext' value updates, and 14 tests were added. 29/29 pass under plain unittest.
  • refactor(ipv6): rename IPv6_GenericExt to IPv6_Ext and make it the base for all 8 extension headers #917's required subclass-contract test exists and exceeds the ask — test_every_subclass_defines_the_required_members_itself walks IPv6_Ext.__subclasses__() transitively and asserts each member is in the subclass's own __dict__, not merely resolvable via getattr, which would hide an inherited fallback. It covers protocol and length on top of the name/alias/__index__ you asked for.
  • All three renames recorded as renames, R060-R091, none fell back to delete+add; toctree points at ipv6_ext with no dangling entry.
  • Coverage: ipv6_ext.py 88 statements at 100%, ah.py 100%, esp.py 93% with none of the new lines among the misses.

One item UNVERIFIED, not blocking: no side-by-side esp.py coverage baseline against ada85bbfc. No regression was inferred from the new lines being absent from the miss list, which is strong but not a baseline diff.

Also worth recording, since I checked it rather than relaying: the reviewer's "main" comparison ran against a different checkout at 3766c3c09. #919 touched no __init__.py, so the import result is unaffected by that gap — I re-derived it on ada85bbfc myself.

@JarryShaw JarryShaw added review: good-to-go Cross-review at the current head says ready; CI state is separate needs: decision Waiting on the maintainer to decide — not blocked by other work 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

No more IPv6_GenericExt name. Its an intermediate state and never released.

@JarryShaw

Copy link
Copy Markdown
Owner Author

Ruling recorded, verbatim:

No more IPv6_GenericExt name. Its an intermediate state and never released.

Verified, and it retires the concern entirely rather than merely overruling it. The name never shipped:

git log --diff-filter=A -- pcapkit/protocols/internet/ipv6_generic_ext.py
  b3551cb63  2026-09-29  fix(ipv6): parse unrecognised extension headers generically (#891) (#904)

Added today, by #904. And across every release tag — v1.4.1.post2, v1.5.0a1, v1.5.0b1 through v1.5.0b6 — the file is absent and the name has zero mentions in pcapkit/protocols/internet/__init__.py.

So the break the cross-review flagged has no victims. There is no third-party code that can import IPv6_GenericExt, and no pickle in existence naming pcapkit.protocols.internet.ipv6_generic_ext.IPv6_GenericExt — the window between #904 landing and this PR is a few hours inside main, with no release cut in it. A deprecation alias would preserve a name nobody ever had.

Removing needs: decision. review: good-to-go stands at 956fb9afe, and the reviewer's caveat is answered by fact rather than by judgement: bare rename is correct here.

@JarryShaw JarryShaw removed the needs: decision Waiting on the maintainer to decide — not blocked by other work label Sep 29, 2026
@JarryShaw

Copy link
Copy Markdown
Owner Author

I think on subclassing, we might wanna keep this convention: if the IPv6 extension header is only usable as an extension header, then it only inherit from IPv6_Ext, like IPv6_Frag; but if it is useable as a standalone protocol itself, then it herit from both IPv6_Ext and Internet (or IPsec), like ESP.

@JarryShaw

Copy link
Copy Markdown
Owner Author

Ruling recorded, verbatim:

I think on subclassing, we might wanna keep this convention: if the IPv6 extension header is only usable as an extension header, then it only inherit from IPv6_Ext, like IPv6_Frag; but if it is useable as a standalone protocol itself, then it herit from both IPv6_Ext and Internet (or IPsec), like ESP.

The PR already satisfies this for six of the eight, including both of your examples. Current bases:

inherits headers
IPv6_Ext only HOPOPT, IPv6_Route, IPv6_Frag, IPv6_Opts, HIP, MH
IPsec + IPv6_Ext AH, ESP

IPv6_Frag extension-only and ESP double-inheriting is exactly as you describe, and AH follows ESP correctly — IPsec, usable over IPv4.

But the convention cannot be settled mechanically, and I want to flag one probable misfit rather than guess. Two measurements, both on 956fb9afe:

  • The dispatch registry does not discriminate. IPv4.__proto__ is Internet.__proto__ → True, so all eight are reachable as an IPv4 payload, HOPOPT and IPv6_Frag included. Registry membership therefore says nothing about standalone-ness; the classification is semantic, per RFC, not derivable from the code.
  • Only MH carries a version-6 gate of its own — 1 mention of a version == 6 check in mh.py, zero in the other seven. That is evidence MH really is IPv6-only, so IPv6_Ext alone looks right for it.

HIP is the one I would question. RFC 7401 HIP is a protocol in its own right, carried directly over IPv4 as well as IPv6, and it is not an IPv6 extension header in the strict sense — which by your rule would put it in the ESP column, inheriting IPv6_Ext + Internet. It currently inherits IPv6_Ext only. I have not changed it, because whether HIP counts as "useable as a standalone protocol itself" here is your call, not a measurement.

Labelled needs: decision on that one question. If HIP should double-inherit I will make the change on this branch; if the convention is meant as guidance for future headers rather than a reclassification of HIP, say so and this merges as-is. Either way the convention itself belongs in docs/source/contributing/conventions.rst — #918 is the issue that harvests settled rulings into it, and I have noted it there.

@JarryShaw JarryShaw added the needs: decision Waiting on the maintainer to decide — not blocked by other work label Sep 29, 2026
@JarryShaw
JarryShaw force-pushed the refactor/917-ipv6-ext-base branch from 956fb9a to b7090ea Compare September 29, 2026 16:18
@JarryShaw

Copy link
Copy Markdown
Owner Author

Rebased onto 5a4f8ca59 — which now carries #921 — clearing BEHIND. New head b7090eae0, one commit, no conflicts and no content change.

Verified after the rebase: test_ipv6_ext_unit 29 OK, test_construction_keyword_check_unit 22 OK, and all four source/docs renames still detected as renames — R064, R082, R060, R091, none degraded to delete-plus-add.

review: good-to-go stays, and needs: decision still stands on the one open question: whether HIP should double-inherit under the convention you just set.

@JarryShaw

Copy link
Copy Markdown
Owner Author

So my suggestion is to read through the RFCs to figure out any of the defined IPv6 extension headers are extension header only or standalone protocol as well. Then we can decide if they should inherit only IPv6_Ext or additional bases.

@JarryShaw

Copy link
Copy Markdown
Owner Author

Ruling recorded, verbatim:

So my suggestion is to read through the RFCs to figure out any of the defined IPv6 extension headers are extension header only or standalone protocol as well. Then we can decide if they should inherit only IPv6_Ext or additional bases.

Dispatched. The research is running against primary sources rather than my recollection: the IANA "IPv6 Extension Header Types" registry — which is the decisive list of what actually is an extension header — the Protocol Numbers registry and its IPv6-Extension-Header column, RFC 8200 §4, and the per-header RFCs already cited in the modules (RFC 4302/4303 for AH/ESP, 6275 for MH, 7401/7402 for HIP, 5095/6554 for IPv6-Route, 5533 for Shim6).

Two things I asked it to widen beyond your eight, so this is settled once rather than revisited:

  • The headers the library does not yet implement — Shim6 (140), 253 and 254 (experimentation and testing), and 59 (IPv6-NoNxt) — because the convention will be read by whoever adds the next one.
  • Two distinct tests for "standalone", kept apart rather than conflated: whether the header can appear as an IPv4 payload, and whether its own RFC defines it as a protocol in its own right rather than purely a chain element. A header can satisfy the second without the first, and collapsing them is how a wrong classification would get justified.

One thing I explicitly told it not to use as evidence: IPv4.__proto__ is Internet.__proto__ is True in this library, so all eight are already reachable as an IPv4 payload in our dispatch. That is an implementation fact about our registry, not an RFC fact, and it would wrongly make every header look standalone.

HIP is the one I expect to matter most, and it may be a larger finding than the inheritance question: if IANA does not mark 139 in the extension-header column at all, then HIP is not an IPv6 extension header in RFC 8200's sense, and its place under IPv6_Ext is the thing to reconsider rather than which bases it adds.

needs: decision stays on this PR until the table is in and you have ruled. review: good-to-go also stays — the code is correct as written; this is a classification question about which bases it should have, not a defect in what it does.

@JarryShaw

Copy link
Copy Markdown
Owner Author

RFC classification is in. One change the evidence supports: HIP should gain Internet as an explicit base. Everything else is already right. Every claim below I verified myself against the primary source, not relayed.

# header ext hdr per IANA verdict ground
0 HOPOPT yes ext-only neither
43 IPv6-Route yes ext-only neither
44 IPv6-Frag yes ext-only neither
50 ESP yes also standalone IPv4 payload + RFC 8200 §4.5 calls it upper-layer
51 AH yes also standalone IPv4 payload (RFC 4302 §3.1.1)
60 IPv6-Opts yes ext-only neither
135 MH yes ext-only own protocol, but cannot be an IPv4 payload
139 HIP yes also standalone IPv4 payload + own protocol
140 Shim6 yes ext-only same shape as MH
253/254 experimentation yes undefined by construction (RFC 4727)

HIP meets your standalone test on a primary source. RFC 7401 App. C.2 "IPv4 HIP Packet (I1 Packet)" shows Source Address: 192.0.2.1, Next Header: 139, Payload Protocol: 59 — HIP directly as an IPv4 payload, exactly ESP's and AH's shape. And §5.1 line 2135 says "The HIP header is logically an IPv6 extension header." So it is both, and it currently inherits IPv6_Ext alone.

The "HIP is not an extension header" possibility I flagged does not fire. I fetched IANA's extension-header.csv directly: 11 entries, and 139 is among them.

MH is the one your test decides, and it turns on which ground counts. RFC 6275 §6.1.1 defines its checksum over a pseudo-header of "IPv6 header fields" with no IPv4 variant, and the IPv4 equivalent function is not protocol 135 at all — RFC 5944 carries Mobile IPv4 over UDP port 434. So it is a protocol in its own right but structurally cannot be an IPv4 payload. Under your wording — "useable as a standalone protocol itself" — MH goes either way depending on whether IPv4-capability or own-protocol-hood is the operative test. Shim6 (140) is identical, so whichever way you rule covers both.

Two by-products, neither part of this PR:

  • A real IANA inconsistency, and the library is on the wrong side of it. protocol-numbers-1.csv flags 147 BIT-EMU Y as an extension header; extension-header.csv omits it. pcapkit/vendor/ipv6/extension_header.py:57 sources from the former, so ExtensionHeader has 12 members where the authoritative registry has 11. Filed separately.
  • A prose overstatement in this PR: ipv6_ext.py says RFC 8200 §4.5 "says outright" that ESP "is not considered an extension header". The sentence opens "For this purpose,", scoping it to the fragmentation discussion. The conclusion still holds — the next sentence lists ESP among upper-layer headers — but "says outright" overstates the quote.

needs: decision stays: your call whether HIP's base changes in this PR or a follow-up, and which ground MH and Shim6 are judged on.

@JarryShaw

Copy link
Copy Markdown
Owner Author

do the update in this PR.

…ared base (#917)

- `git mv` `pcapkit/protocols/internet/ipv6_generic_ext.py` -> `ipv6_ext.py`, plus
  the matching `data/` and `schema/` modules, docs page and test file; the class,
  its alias (`IPv6-GenericExt` -> `IPv6-Ext`) and its name follow.
- `IPv6_Ext` is now generic in `_PT`/`_ST`, like `IPsec`, and serves both roles:
  the shared base of all 8 IPv6 extension headers and the RFC 6564 fallback.
- Re-parent `HOPOPT`, `IPv6_Route`, `IPv6_Frag`, `IPv6_Opts`, `HIP` and `MH` onto
  `IPv6_Ext[…]`; `AH(IPsec, IPv6_Ext)` and `ESP(IPsec, IPv6_Ext)`.
- Scope the fallback-only members so the base cannot shadow a real header:
  `protocol` and the `version` gate discriminate on `__data__`; `alias` spelled
  out on `HOPOPT`/`MH`/`AH`/`ESP`, which took `ProtocolBase`'s class-name default
  before; `name`/`alias`/`__length_hint__` widened to `str`/`int`; `make` made
  keyword-only; `read`'s `alias`/`error` moved into `**kwargs`.
- `ESP` gains the extension-mode contract: `payload` and `protochain` from the
  base, `protocol` spelled out, so its `_extf` is load-bearing (#895).
- New test class holds every `IPv6_Ext` subclass found at runtime to defining
  `name`, `alias`, `protocol`, `length` and `__index__` itself.

331 passed / 1420 subtests (was 317 / 1346); `ipv6_ext.py` coverage 87% -> 100%;
mypy 321 errors, identical to `main`; pylint 627 messages against `main`'s 629.
@JarryShaw
JarryShaw force-pushed the refactor/917-ipv6-ext-base branch from b7090ea to 1f413a2 Compare September 29, 2026 16:50
@JarryShaw

Copy link
Copy Markdown
Owner Author

Ruling recorded, verbatim:

do the update in this PR.

Done — new head 1f413a2bd, three files, +101/−5.

1. HIP now names Internet explicitly. class HIP(IPv6_Ext[…], Internet[…], …). MRO verified clean: HIP -> IPv6_Ext -> Internet -> ProtocolBase -> Generic -> object, no conflict, and all five contract members still in HIP.__dict__. The docstring records both grounds with citations — :rfc:7401#section-5.1 ("the HIP header is logically an IPv6 extension header") and App. C.2's IPv4 header carrying Next Header: 139 — and says why MH and Shim6 do not qualify.

2. A test that pins the classification, so the convention cannot rot into prose. test_standalone_members_name_a_second_base_and_the_rest_do_not asserts against __bases__, not __mro__ — every member reaches Internet transitively through IPv6_Ext, so an __mro__ check passes for all eight and tests nothing. What the convention encodes is the declaration. STANDALONE_MEMBERS = {'AH', 'ESP', 'HIP'}, with the RFC reasoning in a #: comment and a second test guarding the set from going vacuous on a misspelling.

Proven to fail without the fix — reverting only HIP's second base:

AssertionError: set() is not true : HIP is classified as also usable as a standalone
protocol, so it must name Internet (or a subclass such as IPsec) as an explicit base;
it names only ('IPv6_Ext',)

3. Two prose corrections in ipv6_ext.py. The "says outright" overstatement is gone — it now quotes :rfc:8200#section-4.5's actual "For this purpose," scoping and the following sentence listing ESP among upper-layer headers. And "all twelve IANA-registered codes" was wrong: IANA's registry has eleven, and the twelfth is BIT_EMU (147) arriving from this package generating the enumeration out of the wrong registry. The text now says so and points at #925.

I read your ruling as covering HIP only, and left MH and Shim6 as extension-only. That is the answer ground A gives, and it is the test your own wording emphasised. If you meant that being a protocol in its own right is sufficient on its own, then MH and Shim6 both flip and STANDALONE_MEMBERS grows by two — say so and it is a three-line change.

Verified: 31 tests in test_ipv6_ext_unit OK, 68 across test_isort_clean + test_hip_unit + test_esp_unit + test_ah_unit OK, isort clean. needs: decision removed; review: good-to-go is now stale at this head, so it goes back to review: pending and I am dispatching a confirmation pass.

@JarryShaw JarryShaw added review: pending No verdict for the current head - never reviewed, or the head moved since the last one and removed review: good-to-go Cross-review at the current head says ready; CI state is separate needs: decision Waiting on the maintainer to decide — not blocked by other work labels Sep 29, 2026
@JarryShaw

Copy link
Copy Markdown
Owner Author

GOOD TO GO re-confirmed at 1f413a2bd, and the decisive check I ran myself rather than relaying.

Capture parity — this is what proves the double-base change shadowed nothing. Generated the fixtures in a clean tree at this head and counted every HIP-bearing protochain:

  1  Ethernet:IPv6:HIPv1
 46  Ethernet:IPv6:HIPv2
total: 47

alias still resolves to HIP's own version-derived value, not IPv6_Ext's fallback 'IPv6-Ext'. That was the live risk: ProtoChain.__str__ joins aliases, so a shadowed alias would have silently rewritten every chain string and packet-dict key, and it is the exact failure this PR's author hit earlier on four other headers. 47 matches the pre-change count.

MRO is explicit, not changed. IPv6_Ext.__mro__ already places Internet immediately after it, so C3 linearisation of HIP(IPv6_Ext, Internet) merges to the identical sequence a single-base declaration produces: HIP -> IPv6_Ext -> Internet -> ProtocolBase -> Generic -> object. All five contract members still resolve out of HIP.__dict__, and HIP.__index__() == 139.

Scope, measured by me. The three files are +101/−5 exactly. The other 11 paths in git diff 956fb9afe 1f413a2bd are #921's, arriving via the rebase — that diff restricted to them is byte-identical to git diff ada85bbfc 5a4f8ca59 on the same paths, so the branch adds nothing to them.

Prose corrections check out against live sources. RFC 8200 §4.5 reads "For this purpose, the Encapsulating Security Payload (ESP) is not considered an extension header.", with ESP listed among upper-layer header examples in the next sentence — so the new wording is accurate and "says outright" was the overstatement. IANA's registry has exactly 11 rows with 147 absent, and pcapkit/vendor/ipv6/extension_header.py filters item[3] == 'Y' off the Protocol Numbers CSV — the mechanism the new prose describes, tracked as #925.

One honest nuance from the reviewer, worth recording: test_every_standalone_member_is_in_the_family guards only STANDALONE_MEMBERS ⊆ KNOWN_MEMBERS, i.e. a typo, not the __bases__ test against a vacuous family walk. test_the_family_is_exactly_the_eight_known_members in the same class covers that, so the walk cannot go empty without another test in the same run failing loudly — but the guard is narrower than its name suggests.

31/31 tests pass. review: good-to-go restored at this head. Unmerged and unpublished.

@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
JarryShaw merged commit 93cf940 into main Sep 29, 2026
63 checks passed
@JarryShaw
JarryShaw deleted the refactor/917-ipv6-ext-base branch September 29, 2026 17:13
@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
…se (#918) (#929)

* Retitle *Registry Conventions* -> *House Conventions* and widen the preamble:
  the page now carries a protocol-class ruling as well as `pcapkit.const` ones,
  and records the standing ask that a ruling is written here in the same change
  that implements it.
* Correct the five passages #927 left for this issue: the `FEATCode` name miss
  raises `EnumKeyError` rather than a bare `KeyError`; a `_validate_value`
  rejection propagates unwrapped with no usable `default`; #877's phase 2 has
  landed for 17 of the 24 non-registry enumerations rather than "not happened
  yet"; `TransportProtocol.get` is now only a case fold and `Criticality.get` is
  gone.
* New "What a Failed Lookup Raises" for #923's provenance-and-shape ruling.
* New "Which bases an IPv6 extension header names" for #924's subclassing ruling,
  the RFC census behind it, the retired `IPv6_GenericExt` name, and why ESP is an
  extension header that still cannot short-circuit the chain walk.
* Document `EnumValueError`, which had no `autoexception` entry, so five
  references to it on this page rendered as plain text.

15 new tests pin the checkable claims. tests/project 193 OK,
test_sentinel_exports_unit 18 OK, test_ipv6_ext_unit + FEATCode + enum-lookup-base
77 OK; docs build clean, every new cross-reference resolved in the rendered HTML.
@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

breaking Breaks public-facing behaviour or API (apply alongside the type label) docs Pull requests that change documentation only (docs: subject prefix) refactor Restructuring for its own sake — neither a fix nor a new capability (refactor: 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.

refactor(ipv6): rename IPv6_GenericExt to IPv6_Ext and make it the base for all 8 extension headers

1 participant