docs(changelog): shared 1.5.0 changelog — long-lived, merges last (#610, #616, #617, #618, #620) - #657
Open
JarryShaw wants to merge 30 commits into
Open
docs(changelog): shared 1.5.0 changelog — long-lived, merges last (#610, #616, #617, #618, #620)#657JarryShaw wants to merge 30 commits into
JarryShaw wants to merge 30 commits into
Conversation
This was referenced Sep 22, 2026
The bullet #634 originally carried, moved here verbatim so that #634 touches only `pcapkit/protocols/transport/tcp.py` and its two test files. Covers: `TCP.read` seeding its connection-flag accumulator with a `typing.cast` no-op rather than `Flags(0)`, so a flagless segment left `self._flags` a plain `int`. 35 lines added to the entry file; `CHANGELOG.md` regenerated, not edited.
The bullet #635 originally carried, moved here verbatim so that #635 touches only the two `Frame` modules, `pcapkit/toolkit/pcapng.py` and its two test files. Covers: the breaking change to a public attribute -- `Frame.len` is the on-wire length and `cap_len` the captured one, which the PCAP and PCAP-NG readers had filled from opposite wire fields. 41 lines added to the entry file; `CHANGELOG.md` regenerated, not edited.
The bullet #636 originally carried, moved here verbatim so that #636 touches only `pcapkit/foundation/extraction.py` and its two test files. Covers: `Extractor` closing the caller's input stream and leaking the one it opened itself, both handlers now reading a single `_owns_input` predicate. 23 lines added to the entry file; `CHANGELOG.md` regenerated, not edited.
The bullet #639 originally carried, moved here verbatim so that #639 touches only `pcapkit/foundation/extraction.py`, `pcapkit/interface/core.py` and its three test files. Covers: `extract(..., no_eof=True)` never returning, and the progress check that now ends it -- including the deliberate narrowing for a seekable input still being appended to. 43 lines added to the entry file; `CHANGELOG.md` regenerated, not edited.
The bullet #640 originally carried, moved here verbatim so that #640 touches only `pcapkit/protocols/protocol.py`, `pcapkit/protocols/application/http.py`, `docs/source/ext.rst`, `examples/generators/dispatch.py` and its six test files. Covers: the behaviour change to a public API -- building a protocol through its constructor with a keyword no signature declares now raises `UnsupportedCall` instead of discarding it. 55 lines added to the entry file; `CHANGELOG.md` regenerated, not edited.
JarryShaw
force-pushed
the
docs/changelog-1.5.0
branch
from
September 22, 2026 17:15
f846523 to
69a6e13
Compare
This was referenced Sep 22, 2026
Merged
JarryShaw
added a commit
that referenced
this pull request
Sep 22, 2026
…t values (#653, #654, #655) `_make_param_puzzle` and `_make_param_solution` derived three wire-format quantities from the payload value rather than taking them from the data model. All three derivations were wrong, in the same two functions, and they are fixed together because the width resolution is one expression that cannot be written twice. * The field width came from `int.bit_length()` and nothing else, so every leading zero octet was dropped on re-serialisation. A SOLUTION read with `Length = 20` rebuilt as `Length = 6`, a PUZZLE read with `Length = 12` as `Length = 5` -- silently, since the integers survive and nothing raises. Both data models now carry `rhash_len`, the field's on-wire width in bits, and both builders prefer it. This only became reachable end to end once #608 was fixed (#629); before that the undersized rebuild tripped the reader's parity guard first and failed loudly. (#653) * SOLUTION's second contents octet is `Reserved`, "zero when sent, ignored when received" (RFC 7401 5.2.5, RFC 5201 5.2.5 identically) -- not a PUZZLE `Lifetime`, which only RFC 7401 5.2.4 defines. pcapkit wrote `0x20`, `0x21`, `0x25` or `0x2b` there and could not write the mandated zero at all: a conformant SOLUTION with `Reserved = 0x00` parsed to `timedelta(0)` and then escaped a bare `ValueError` from `math.log2(0.0)`. `Data_SolutionParameter` and `SolutionParameter` now carry `reserved` verbatim, so the conformant zero round-trips and a received non-zero octet is reproduced rather than re-derived. (#654) * Neither builder read its own `version` keyword, so `version=1` and `version=2` computed identical lengths at every bit width. RFC 5201 5.2.4 and 5.2.5 state both fields as literally 8 bytes and `Length` as literally 12 and 20, and the readers enforce exactly that -- so under HIPv1 each builder accepted only a `bit_length()` of 57..64 and built, for everything else, a parameter this library's own reader rejects. Width now comes from the version under HIPv1. (#655) The remaining `math.log2` sites, both in PUZZLE where a `Lifetime` really lives, now raise `ProtocolError` rather than letting `ValueError` escape. `ProtocolError(BaseError, ValueError)` is what the readers already raise for a malformed PUZZLE or SOLUTION, and deriving from the builtin it replaces keeps any caller written around today's bare `ValueError` working; `EnumError` is `(BaseError, TypeError)` and would silently stop being caught. The same guard covers the upper end, because `UInt8Field` wraps rather than raising -- measured, `300` packs as `0x2c` -- so an out-of-range lifetime would otherwise be written as some other valid-looking duration. A plain `float` lifetime used to escape an `AttributeError` from the `isinstance(lifetime, int)` branch; the test keyed on `timedelta` instead found that. `_make_param_solution` no longer accepts `lifetime=`. `reserved=` and `rhash_len=` are both `None`-sentinelled, so an explicit value overrides what a parsed parameter carries: `Data_SolutionParameter` is immutable, so without that a caller holding a parsed parameter had no way to write the conformant zero over a peer's non-conformant `Reserved`. The plain data fields still let `param` win, as they did before. `rhash_len=` is also the only way a from-scratch HIPv2 build can state a width, which genuinely varies with the Responder's HIT Suite (RFC 7401 2.3, 5.2.10). Five new tests, each verified to fail on 0c7f2b7 for the defect's own reason and pass here: `12 != 5` and friends for #655, `expected a positive input` and `'reserved' not found` for #654, `no attribute 'rhash_len'` for #653. The widths exercised are 1, 9, 15, 17, 57 and 65 bits, where the candidate formulas disagree, with the byte-aligned widths kept as controls -- at a multiple of 8 the correct `2 * ceil(b / 8)` coincides with #608's `ceil(b / 4)`, which is why every byte-aligned fixture passed through that defect unharmed. Two cases are deliberately accepted rather than rejected, and now say so in `_make_puzzle_field_width`'s docstring: `rhash_len == 0`, which is what the derived path yields for the default `random=0` and what a `Length = 4` parameter parses back to; and a `version` other than 1, which is treated as HIPv2 exactly as both readers' `version == 1` guards do. Coverage holds at 100% statement and branch on all three changed modules, with statements 1385 -> 1414 and branches 316 -> 338; 30 tests / 434 subtests -> 35 / 455. Scoped unit tier green: 291 passed, 1191 subtests. No EXPECTED_FAILURES entry moved -- still 45, with the same four HIP entries. No changelog entry: that is consolidated in #657.
JarryShaw
added a commit
that referenced
this pull request
Sep 22, 2026
…t values (#653, #654, #655) `_make_param_puzzle` and `_make_param_solution` derived three wire-format quantities from the payload value rather than taking them from the data model. All three derivations were wrong, in the same two functions, and they are fixed together because the width resolution is one expression that cannot be written twice. * The field width came from `int.bit_length()` and nothing else, so every leading zero octet was dropped on re-serialisation. A SOLUTION read with `Length = 20` rebuilt as `Length = 6`, a PUZZLE read with `Length = 12` as `Length = 5` -- silently, since the integers survive and nothing raises. Both data models now carry `rhash_len`, the field's on-wire width in bits, and both builders prefer it. This only became reachable end to end once #608 was fixed (#629); before that the undersized rebuild tripped the reader's parity guard first and failed loudly. (#653) * SOLUTION's second contents octet is `Reserved`, "zero when sent, ignored when received" (RFC 7401 5.2.5, RFC 5201 5.2.5 identically) -- not a PUZZLE `Lifetime`, which only RFC 7401 5.2.4 defines. pcapkit wrote `0x20`, `0x21`, `0x25` or `0x2b` there and could not write the mandated zero at all: a conformant SOLUTION with `Reserved = 0x00` parsed to `timedelta(0)` and then escaped a bare `ValueError` from `math.log2(0.0)`. `Data_SolutionParameter` and `SolutionParameter` now carry `reserved` verbatim, so the conformant zero round-trips and a received non-zero octet is reproduced rather than re-derived. (#654) * Neither builder read its own `version` keyword, so `version=1` and `version=2` computed identical lengths at every bit width. RFC 5201 5.2.4 and 5.2.5 state both fields as literally 8 bytes and `Length` as literally 12 and 20, and the readers enforce exactly that -- so under HIPv1 each builder accepted only a `bit_length()` of 57..64 and built, for everything else, a parameter this library's own reader rejects. Width now comes from the version under HIPv1. (#655) The remaining `math.log2` sites, both in PUZZLE where a `Lifetime` really lives, now raise `ProtocolError` rather than letting `ValueError` escape. `ProtocolError(BaseError, ValueError)` is what the readers already raise for a malformed PUZZLE or SOLUTION, and deriving from the builtin it replaces keeps any caller written around today's bare `ValueError` working; `EnumError` is `(BaseError, TypeError)` and would silently stop being caught. The same guard covers the upper end, because `UInt8Field` wraps rather than raising -- measured, `300` packs as `0x2c` -- so an out-of-range lifetime would otherwise be written as some other valid-looking duration. A plain `float` lifetime used to escape an `AttributeError` from the `isinstance(lifetime, int)` branch; the test keyed on `timedelta` instead found that. `_make_param_solution` no longer accepts `lifetime=`. `reserved=` and `rhash_len=` are both `None`-sentinelled, so an explicit value overrides what a parsed parameter carries: `Data_SolutionParameter` is immutable, so without that a caller holding a parsed parameter had no way to write the conformant zero over a peer's non-conformant `Reserved`. The plain data fields still let `param` win, as they did before. `rhash_len=` is also the only way a from-scratch HIPv2 build can state a width, which genuinely varies with the Responder's HIT Suite (RFC 7401 2.3, 5.2.10). Five new tests, each verified to fail on 0c7f2b7 for the defect's own reason and pass here: `12 != 5` and friends for #655, `expected a positive input` and `'reserved' not found` for #654, `no attribute 'rhash_len'` for #653. The widths exercised are 1, 9, 15, 17, 57 and 65 bits, where the candidate formulas disagree, with the byte-aligned widths kept as controls -- at a multiple of 8 the correct `2 * ceil(b / 8)` coincides with #608's `ceil(b / 4)`, which is why every byte-aligned fixture passed through that defect unharmed. The byte-exact assertions compare the parameter without its trailing padding, and then against the re-packed source schema rather than against a literal, so they pin the `Length` field and the payload octets without encoding a padding rule that #651/#664 is concurrently changing. Verified against a `git merge-tree` of this branch and #664: both library files auto-merge with no conflict, and all five new tests pass against the merged library. Two cases are deliberately accepted rather than rejected, and now say so in `_make_puzzle_field_width`'s docstring: `rhash_len == 0`, which is what the derived path yields for the default `random=0` and what a `Length = 4` parameter parses back to; and a `version` other than 1, which is treated as HIPv2 exactly as both readers' `version == 1` guards do. Coverage holds at 100% statement and branch on all three changed modules, with statements 1385 -> 1414 and branches 316 -> 338; 30 tests / 434 subtests -> 35 / 455. Scoped unit tier green: 291 passed, 1191 subtests. No EXPECTED_FAILURES entry moved -- still 45, with the same four HIP entries. No changelog entry on this branch: that is consolidated in #657.
…ships in #665 The bullet #665 would otherwise have carried, kept here so that #665 touches only `pcapkit/protocols/internet/hip.py`, `pcapkit/protocols/schema/internet/hip.py`, `pcapkit/protocols/data/internet/hip.py` and `tests/protocols/internet/test_hip_unit.py`. Covers all three as one bullet, because they are one root cause: the HIP `PUZZLE` and `SOLUTION` builders derived the field width, the `Reserved` octet and the version-dependent length from the payload value instead of from the data model. Splitting the entry would tell the story three times and explain it none. Two public data models change, so the bullet says so in bold and carries a migration sentence: `SolutionParameter.lifetime` becomes `reserved` and an `int` rather than a `timedelta`, both parameter models gain a required `rhash_len`, and `_make_param_solution` no longer takes `lifetime=`. 46 lines added to the entry file; `CHANGELOG.md` regenerated with `util/changelog_md.py`, not edited. `--check` exits 0 and `tests/project/` is green at 96 passed, 469 subtests. Committed from a detached HEAD on 69a6e13 and pushed to the branch ref, because `docs/changelog-1.5.0` is checked out in another agent's worktree at a stale f846523 and could not be taken here.
The bullet #664 would otherwise have carried, kept here so that #664 touches only `pcapkit/protocols/internet/hip.py`, `pcapkit/protocols/schema/internet/hip.py`, `tests/protocols/internet/test_hip_unit.py`, `tests/protocols/test_option_roundtrip_unit.py`, `examples/generators/options.py` and `docs/source/pcapkit/protocols/internet/hip.rst`. One bullet, because it is one root cause in 95 places: every HIP padding site aligned the parameter's *contents* to eight octets rather than the record, ignoring the four-octet type-and-length header, so every parameter pcapkit wrote was `4 (mod 8)` for every possible `Length`. The bullet says in bold that both the emitted octets and the data model's reported `length` change, and carries a migration sentence: a `SEQ` parameter's `length` is 8 where it was 12, so code comparing stored output byte for byte or asserting on `Data_*Parameter.length` sees different values. It also records what was deliberately *not* changed, since both look like part of the same defect and are not: `HIP.make`'s `len = total_length // 8 + 4`, which RFC 7401 section 5.1.3 shows is correct and merely needed 8-aligned parameters; and `HIP_COPIES`, which stays at two for `R1_COUNTER`'s four-octet `counter` against section 5.2.3's eight -- a separate, still-unfiled defect this one had been masking. The `EncryptedParameter.data` length callback is named as fixed in the same change because the two four-octet errors cancelled at four of the eight residues of `Length`, so correcting the padding alone would have regressed it. 41 lines added to the entry file; `CHANGELOG.md` regenerated with `util/changelog_md.py`, not edited. `--check` exits 0 and `tests/project/` is green at 96 passed, 469 subtests. Committed from a detached HEAD on 2ce3687 and pushed to the branch ref, because `docs/changelog-1.5.0` is checked out in another agent's worktree at a stale f846523 and could not be taken here. Note 2ce3687, not the 69a6e13 I was given: the branch had already moved on with #665's entry.
The bullet #667 would otherwise have carried, kept here so that #667 touches only `pcapkit/corekit/multidict.py` and `tests/corekit/test_multidict.py`. One bullet, because it is one convention gap in one class: `_Missing` behind `MultiDict.pop` and `OrderedMultiDict.pop` lacked the `@final` and the falsy `__bool__` that `NoValueType` in `pcapkit.corekit.fields.field` sets as the package's convention for a marker of this kind. The bullet says plainly that no behaviour changes, and says why rather than asserting it: both `pop()` implementations decide by identity, never by truthiness, and `pop()` structurally cannot return the marker -- it returns `default` only on the branch where `default is not _missing`. It also names the one way the old truthiness was observable, which is what justifies touching it at all: `inspect.signature(MultiDict.pop).parameters['default'].default` hands the marker to any caller who asks, and `if default:` on it reported "a default was supplied" where none had been. It closes by recording the disposition of the other two sites from the #640 sweep, so the entry is the whole story: site 1 needed nothing, and `_NOT_FOUND` in `pcapkit.utilities.compat` stays a bare `object()` deliberately, being a verbatim line of CPython's `functools.cached_property` inside a `sys.version_info < (3, 8)` branch no supported interpreter reaches. The reasoning behind that one is on #661, not here. `:obj:` roles had to come out: `util/changelog_md.py` rejects them with `ResidualMarkupError`, since its six conversion rules do not cover interpreted text and `CHANGELOG.md` would carry the role through as literal text. Double backticks instead, which is what the rest of the entry file uses. 20 lines added to the entry file; `CHANGELOG.md` regenerated with `util/changelog_md.py`, not edited. `--check` exits 0 and `tests/project/` is green at 96 passed, 469 subtests. Committed from a detached HEAD on e55ba36 and pushed to the branch ref, because `docs/changelog-1.5.0` is checked out in another agent's worktree at a stale f846523 and could not be taken here. Note e55ba36, not the 69a6e13 I was given: the branch had already moved on with #665's and #651's entries. Refs #661
#669 One bullet, because it is one round trip with a defect on each side of it, in the same two files: `_make_http_data` never read `frame.flags` on the construct side, and `FrameType.post_process` seeded its accumulator with a bare `0` on the parse side. The bullet leads with what changes rather than with the mechanism, since both halves alter output: the reconstructed DATA frame's flags octet, and the dumped `__value__` of a flagless frame. It says why #650 was worth fixing at all, which its issue had left as an open question -- the dump rendered `__value__` as a JSON number for a flagless frame and a JSON string for every other frame in the same capture, so the fix removes a type inconsistency rather than introducing one. It also records two things a reader would otherwise be surprised by. The seed is guarded rather than unconditional, because `FrameType.Flags` has no members and a memberless `enum.Flag` subclass refuses `Flags(0)` -- the one-token fix the issue proposed would have crashed six of the twelve frame schemas. And a DATA round trip is still lossy after this, for the unrelated mis-parenthesised length callbacks filed as #668, so the entry does not let the reader infer a clean round trip that does not exist yet. The `TypeError` message had to sit on one line: `util/changelog_md.py` rejects a `` literal spanning a line break with `ResidualMarkupError`, since its six conversion rules do not cover it. 37 lines added to the entry file; `CHANGELOG.md` regenerated with `util/changelog_md.py`, not edited. `--check` exits 0 and `tests/project/` is green at 96 passed, 469 subtests. Committed from a detached HEAD on 367b6e6 and pushed to the branch ref, because `docs/changelog-1.5.0` is checked out in another agent's worktree and could not be taken here. Note 367b6e6, not the 69a6e13 I was given: the branch had already moved on with #665's, #651's and #661's entries. Refs #652 Refs #650
…s in #670 and #671 Two bullets, not one, because the two defects are unrelated: one changes what the dumpers emit, the other only the text of four exception messages. They share a file only by accident of being found in the same pass. The #648 bullet leads with the output change and says so in bold, because that is what a reader upgrading needs to see: a flag value with no declared bits dumped as `Type::None [0]` in all six textual format names, out of both `Extractor` and `TraceFlow`. It then justifies the *replacement* rather than just stating it, since "render it as its decimal value" looks arbitrary until you know the enumeration libraries already spell an undeclared residue that way -- and that a decimal cannot collide with a member name where `None` can, `NONE` being a real declared name elsewhere. Three things a reader would otherwise get wrong are recorded: three sites carried the interpolation and not one, the guard is on `name is None` rather than on zero because the defect never was about zero, and it is not an `aenum` quirk since stdlib `enum.IntFlag` behaves identically. It also corrects the issue on a point of fact. #648 said `Flags` was the only registry nameless at zero; a sweep of all seven finds five, the four Mobility Header flag registries included. And it states that the committed example dumps do not move, which was measured by regenerating all three with and without the change rather than assumed -- a reader of a bullet this emphatic will otherwise wonder whether `examples/captures/` drifted. The #649 bullet says "cosmetic" in its second sentence so nobody reads it as a behavioural change, then gives the one reason it was worth doing at all: it is the text a user sees when an option is rejected. It names all four sites, and the 28-against-4 count in the same file, because that count is what makes the correct form a fact about the module rather than a preference. Neither bullet claims a guard it does not have: #648's third site, the `addon` branch, is not reachable from any registry in the library today, and the bullet does not imply otherwise. `CHANGELOG.md` regenerated with `util/changelog_md.py`, not edited by hand. `--check` exits 0 and `tests/project/` is green at 96 passed, 469 subtests. Each `` literal is kept on one line, since the generator rejects one spanning a line break with `ResidualMarkupError`. Committed from a detached HEAD on d14577d and pushed to the branch ref, because `docs/changelog-1.5.0` is checked out in another agent's worktree and could not be taken here. Note d14577d, not the 69a6e13 I was given: the branch had already moved on with #665's, #651's, #661's and #652/#650's entries. Refs #648 Refs #649
…entry The bullet added in d14577d said a memberless `enum.Flag` subclass "refuses `Flags(0)` outright", flatly. That is only true from Python 3.12, where the enum rewrite made `EnumType.__call__` raise for an enum with no members; earlier interpreters take the plain value-lookup path and hand back a pseudo-member. Measured on the two available here: version 3.14.7 members: 0 Flags(0) -> TypeError: <flag 'Flags'> has no members version 3.7.16 members: 0 Flags(0) -> OK <Flags.0: 0> `requires-python` is `>=3.6`, so the unqualified form overstated it. Three words added, no other change to the bullet: the guard in #669 is correct on every supported interpreter either way, being keyed on the memberless-ness rather than on the refusal. The same imprecision was corrected in #669's own source comment and PR body, and its test now gates only the `TypeError` assertion behind `sys.version_info >= (3, 12)` -- CI runs the unit tier on 3.10 through 3.15, so an unconditional `assertRaises` would have gone red on the older two. `CHANGELOG.md` regenerated with `util/changelog_md.py`, not edited; `--check` exits 0 and `tests/project/` is green at 96 passed, 469 subtests. Committed from a detached HEAD on 6a956c4 and pushed to the branch ref, because `docs/changelog-1.5.0` is checked out in another agent's worktree. The branch had moved on with #648's and #649's entries since d14577d. Refs #652 Refs #650
JarryShaw
added a commit
that referenced
this pull request
Sep 22, 2026
…a false packaging claim (#642) Three names appeared in string annotations that their module never imported, and they were mypy's complete set of ``name-defined`` findings for the package: * ``pcapkit/utilities/logging.py:350`` used ``Any``; added to the ``TYPE_CHECKING`` block beside ``IO``, ``Optional`` and ``Union``. * ``pcapkit/protocols/schema/internet/ipv6_route.py:136`` used ``Protocol`` and ``:271`` used ``Optional``; added, ``Protocol`` as ``ProtocolBase as Protocol``. Twenty-one sibling schema modules already spell it that way, in the same ``payload:`` stub; this makes twenty-two. mypy 2.3.1 over ``pcapkit``: 3 ``name-defined`` errors before, 0 after; 115 total errors before, 112 after, so nothing else moved. ``MANIFEST.in:14-17`` asserted that ``include README.md`` was "the only thing that puts it in an sdist" and that an sdist without it "cannot be installed at all". Both halves are false, and the file had contradicted itself since #631 wrote the correct mechanism seven lines below without correcting this. Deleting the lines and rebuilding gives a byte-identical sdist listing -- empty diff -- that installs with exit 0: ``setuptools/command/sdist.py:59-60`` ships the README unconditionally and ``setuptools/dist.py:460``'s default ``license_files`` glob ships ``LICENSE``. Of the original three ``include`` lines only ``CHANGELOG.md`` is load-bearing. The comment now says that. New ``tests/project/test_annotation_names.py`` resolves every string annotation in the package -- following a nested forward reference such as ``'list["Nested"]'``, while treating ``Literal`` members and ``Annotated`` metadata as the values they are -- against the names its own module binds. It reports the same three findings as mypy on the unfixed tree and none after. That module named ``ast.TypeAlias`` and ``ast.TypeVar`` directly, and both are PEP 695 nodes added in Python 3.12, so *every* test in it raised ``AttributeError`` on the 3.10 and 3.11 matrix jobs -- ``bound_names`` walks every node of every file, so the attribute is reached whatever a test does. Both are now resolved once at module scope through ``getattr(ast, ..., ())``, leaving the ``isinstance`` branches otherwise untouched: ``isinstance(x, ())`` is always False, so the branches stay live on 3.12+ and are simply unreachable below it. Chosen over a ``sys.version_info`` comparison because it writes no version number down at all -- a comparison states 3.12 next to the attribute it guards, and the two can then drift -- and over a per-node ``getattr`` because a module-level constant lifts the lookup out of a loop that runs on every node of every file. ``ast.TypeVar`` is the branch that earns its keep: it carries its name as a bare ``str`` and emits no ``ast.Name`` node, so forcing ``_TYPE_VAR`` to ``()`` on 3.14.7 turns ``T`` and ``U`` into false findings. ``ast.TypeAlias`` is defensive by comparison -- its name *is* an ``ast.Name`` in ``Store`` context, which the preceding branch already catches -- and is left as it stands rather than removed. A new ``test_a_pep695_type_parameter_is_in_scope`` pins both the guards and the behaviour, skipped below 3.12 because its fixture source cannot parse there. Measured on real interpreters rather than simulated. 3.10.21 and 3.11.15: 5 failed, exit 1 -> 5 passed, 1 skipped, exit 0. 3.14.7: all 6 pass, exit 0. 133 passed over ``tests/project`` and ``tests/utilities/test_logging.py``, exit 0, subtests unchanged at 487. No changelog entry on this branch. Per the rule that no code branch touches ``CHANGELOG.md`` or anything under ``docs/source/changelog/``, this change's entry -- and the wording correction the ``MANIFEST.in`` claim implies for the #619 entry, plus the missing ``(#570)`` and ``(#577)`` citations -- go to the shared changelog pull request #657 instead.
The `pypi` job's `environment: release` was commented out and `conda` never had one, so a scheduled vendor bump could publish to PyPI and Anaconda unapproved. Four jobs are now gated, one environment per credential, and the entry says plainly that the gate is inert until the environments carry required reviewers. Regenerated CHANGELOG.md with `util/changelog_md.py`; `--check` exits 0.
…11, not 3.12 b2ec64b qualified the claim with the wrong version. The refusal does not start with a 3.12 change -- it starts at 3.11, and 3.12 only reworded the message. The cross-review on #669 caught it; I had inferred 3.12 from the message text I happened to measure on, which is the wrong evidence for a boundary. Measured across every interpreter available here rather than inferred, with a bare `class Flags(enum.IntFlag): pass`: 3.8.20 Flags(0) -> OK <Flags.0: 0> 3.9.25 Flags(0) -> OK <Flags.0: 0> 3.10.21 Flags(0) -> OK <Flags.0: 0> 3.11.15 Flags(0) -> TypeError: <flag 'Flags'> has no members defined 3.12.13 Flags(0) -> TypeError: ... has no members; specify `names=()` ... 3.14.7 Flags(0) -> TypeError: ... has no members; specify `names=()` ... Confirmed in CPython's source, not just behaviourally. 3.11's `enum.py:1117`, inside `Enum.__new__`, raises `TypeError("%r has no members defined" % cls)` when `not cls._member_map_`, and it runs *before* the `_missing_` hook that manufactured the pseudo-member on 3.10. 3.10's `enum.py` has no such raise -- its only "has no members" occurrence is a comment at :616. 3.11 is also where the metaclass was renamed (`class EnumType(type)` at :479 with `EnumMeta = EnumType` at :1052, against 3.10's `class EnumMeta(type)` at :161), so "the enum rewrite" is the 3.11 release. One word in the bullet. #669 carries the matching correction to its source comment and to its test's `sys.version_info` gate, which had been skipping the assertion on 3.11 -- a version the unit-test matrix runs -- even though 3.11 does raise. `CHANGELOG.md` regenerated with `util/changelog_md.py`, not edited; `--check` exits 0 and `tests/project/` is green at 96 passed, 469 subtests. Committed from a detached HEAD on b2ec64b and pushed to the branch ref, because `docs/changelog-1.5.0` is checked out in another agent's worktree. Refs #652 Refs #650
The changelog changes #666 was carrying on its own branch, moved here so that #666 touches only `MANIFEST.in`, `pcapkit/protocols/schema/internet/ipv6_route.py`, `pcapkit/utilities/logging.py` and `tests/project/test_annotation_names.py`. #666 was the last open branch still editing `CHANGELOG.md` itself; it no longer does. Four pieces, not one, because #666 had amended existing entries as well as needing a new one. A new **Fixed** bullet for #642: three names used in string annotations that their own module never imported -- `Any` in `pcapkit/utilities/logging.py`, and `Protocol` and `Optional` in `pcapkit/protocols/schema/internet/ipv6_route.py`. The bullet names the `typing.cast` case specifically, because that is the one no running test can catch: `cast` never evaluates its first argument. It states plainly that nothing resolves at runtime that did not before, since `TYPE_CHECKING` is `False` when the interpreter runs, so that the entry is not read as a runtime fix. mypy 2.3.1's before/after is quoted as the measurement -- three `name-defined` errors to none, 115 total to 112 -- and the new `tests/project/test_annotation_names.py` is described as what pins the invariant without a type checker installed. Three missing citations recovered: `(#570)` on the L2TPv3 worked-example line, `(#577)` on the `register_extractor_engine` keyword line, and `(#619)` on the README rename entry. And the #619 entry's packaging claim corrected in place. It asserted that `include README.md` in `MANIFEST.in` was "the only thing that puts the README in a source distribution" and that an sdist without it "cannot be installed". Both halves are false: setuptools' own `sdist` command ships the README before `MANIFEST.in` is read at all, so dropping the line leaves the listing byte-identical at 861 entries, and `setup.py` reads the file from wherever it is executing, which for a `pip` install of an sdist is the unpacked sdist. #666 corrects the same claim in the `MANIFEST.in` comment, so the two stay in step. No `:pep:` role, though the new bullet discusses PEP 695: `util/changelog_md.py` converts only double-backtick literals and the `:rfc:` role, and raises `ResidualMarkupError` on anything else, exactly as the #661 entry hit with `:obj:`. Plain prose instead. 50 lines added to the two files, 11 reflowed. `CHANGELOG.md` regenerated with `util/changelog_md.py`, not edited; `--check` exits 0 and `tests/project/` is green at 96 passed, 469 subtests, exit 0 -- the same counts the previous commit on this branch reported, so nothing else moved. Committed from a detached HEAD and pushed to the branch ref, because `docs/changelog-1.5.0` is checked out in another agent's worktree at a stale f846523 and could not be taken here. Written against b2ec64b and rebased onto d47ada0, the branch having taken #641's entry and the #652/#650 boundary correction in the meantime. Both files conflicted, both at the append point rather than in substance -- #641's bullet and this one land in the same place at the end of **Fixed** -- so the resolution keeps both, #641's first. `CHANGELOG.md` was not hand-resolved: it is generated, so it was regenerated from the resolved entry file and `--check` re-run, which is the only resolution that cannot drift. Refs #642
Closes the residual the #573 entry above records under "What this does not close": the 16-bit band, where a shortfall of 65,536 octets or fewer is padded unconditionally and charged to nothing, so an option declaring 65,535 octets could be repeated per block without limit. `util/changelog_md.py` regenerated `CHANGELOG.md`; `--check` exits 0.
Records the six constant registries that rejected an invalid value in a way the built-in `enum` does not -- three composing a pseudo-member for any integer at all -- and why the issue's own proposal to raise `EnumError` was rejected rather than adopted. Also corrects the #623 entry above, which stated that `pcapkit/const/tcp/flags.py` "defines no `_missing_`". It defined none at the time and never had #623's recursion defect, which is what that sentence was about, but it has one now; the tense is fixed and the new entry named. `CHANGELOG.md` regenerated with `python util/changelog_md.py`; `--check` exits 0.
The entry described only the payload bound. The cross-review of #676 found that the option *area* is sized from a Block Total Length nothing checks against the file, so a block declaring 1,000,000 octets while holding 36 synthesised 65,535 anyway -- 1,820x, unwarned. `bounded_area` closes that and the entry now says so. Also corrects two claims the same review disproved: the negative-remainder skip is load-bearing on the unpacking path rather than the packing one, and the truncation sweep is not uniformly one exception type. `util/changelog_md.py` regenerated `CHANGELOG.md`; `--check` exits 0.
…ly fixes #664 was narrowed per the owner's decision in #679: `LOCATOR_SET` is excluded and keeps the pre-#651 padding expression, so the change corrects 45 of the 46 HIP parameters rather than all of them. This entry claimed all of them, which would have shipped a false statement about wire output in the release notes. Three claims narrowed, in place rather than as a second bullet: - the opening, from "every HIP parameter" to 45 of 46, naming the exclusion; - the site counts, from "the 46 callbacks and the 49 record lengths ... instead of 95 times" to 93 of the 95 sites, 45 of 46 and 48 of 49; - the migration sentence, which said every other parameter's `length` moves likewise -- now the other 44, with `LOCATOR_SET` called out as unchanged in both its octets and its data model. Added the reason for the exclusion, because a reader who meets it in the code otherwise cannot tell it from an oversight: two defects in that parameter cancel exactly -- the padding callback never receives the parameter's `len`, since the nested `Locator` schemas share a packet context whose own `len` shadows it, and the parameter's `len` is in 4-octet units where the RFC's `Length` is a byte count. Always-4 padding gives `4 + 24n + 4`, and because `24n` is a multiple of 8 the RFC total for a byte-count `Length` of `24n` is the same `24n + 8`. Measured at n = 1, 2, 5 as 32, 56 and 128 octets on `b34f132f6` and on #664's head alike, so correcting only the padding would have taken a conformant parameter to four octets short. #679 carries the pair. 32 lines changed in the entry file; `CHANGELOG.md` regenerated with `util/changelog_md.py`, not edited. `--check` exits 0 and `tests/project/` is green at 96 passed, 469 subtests. Committed from a detached HEAD on aef0fb9 and pushed fast-forward to the branch ref, because `docs/changelog-1.5.0` is checked out in another agent's worktree and could not be taken here.
JarryShaw
added a commit
that referenced
this pull request
Sep 22, 2026
…t values (#653, #654, #655) (#665) `_make_param_puzzle` and `_make_param_solution` derived three wire-format quantities from the payload value rather than taking them from the data model. All three derivations were wrong, in the same two functions, and they are fixed together because the width resolution is one expression that cannot be written twice. * The field width came from `int.bit_length()` and nothing else, so every leading zero octet was dropped on re-serialisation. A SOLUTION read with `Length = 20` rebuilt as `Length = 6`, a PUZZLE read with `Length = 12` as `Length = 5` -- silently, since the integers survive and nothing raises. Both data models now carry `rhash_len`, the field's on-wire width in bits, and both builders prefer it. This only became reachable end to end once #608 was fixed (#629); before that the undersized rebuild tripped the reader's parity guard first and failed loudly. (#653) * SOLUTION's second contents octet is `Reserved`, "zero when sent, ignored when received" (RFC 7401 5.2.5, RFC 5201 5.2.5 identically) -- not a PUZZLE `Lifetime`, which only RFC 7401 5.2.4 defines. pcapkit wrote `0x20`, `0x21`, `0x25` or `0x2b` there and could not write the mandated zero at all: a conformant SOLUTION with `Reserved = 0x00` parsed to `timedelta(0)` and then escaped a bare `ValueError` from `math.log2(0.0)`. `Data_SolutionParameter` and `SolutionParameter` now carry `reserved` verbatim, so the conformant zero round-trips and a received non-zero octet is reproduced rather than re-derived. (#654) * Neither builder read its own `version` keyword, so `version=1` and `version=2` computed identical lengths at every bit width. RFC 5201 5.2.4 and 5.2.5 state both fields as literally 8 bytes and `Length` as literally 12 and 20, and the readers enforce exactly that -- so under HIPv1 each builder accepted only a `bit_length()` of 57..64 and built, for everything else, a parameter this library's own reader rejects. Width now comes from the version under HIPv1. (#655) The remaining `math.log2` sites, both in PUZZLE where a `Lifetime` really lives, now raise `ProtocolError` rather than letting `ValueError` escape. `ProtocolError(BaseError, ValueError)` is what the readers already raise for a malformed PUZZLE or SOLUTION, and deriving from the builtin it replaces keeps any caller written around today's bare `ValueError` working; `EnumError` is `(BaseError, TypeError)` and would silently stop being caught. The same guard covers the upper end, because `UInt8Field` wraps rather than raising -- measured, `300` packs as `0x2c` -- so an out-of-range lifetime would otherwise be written as some other valid-looking duration. A plain `float` lifetime used to escape an `AttributeError` from the `isinstance(lifetime, int)` branch; the test keyed on `timedelta` instead found that. `_make_param_solution` no longer accepts `lifetime=`. `reserved=` and `rhash_len=` are both `None`-sentinelled, so an explicit value overrides what a parsed parameter carries: `Data_SolutionParameter` is immutable, so without that a caller holding a parsed parameter had no way to write the conformant zero over a peer's non-conformant `Reserved`. The plain data fields still let `param` win, as they did before. `rhash_len=` is also the only way a from-scratch HIPv2 build can state a width, which genuinely varies with the Responder's HIT Suite (RFC 7401 2.3, 5.2.10). Five new tests, each verified to fail on 0c7f2b7 for the defect's own reason and pass here: `12 != 5` and friends for #655, `expected a positive input` and `'reserved' not found` for #654, `no attribute 'rhash_len'` for #653. The widths exercised are 1, 9, 15, 17, 57 and 65 bits, where the candidate formulas disagree, with the byte-aligned widths kept as controls -- at a multiple of 8 the correct `2 * ceil(b / 8)` coincides with #608's `ceil(b / 4)`, which is why every byte-aligned fixture passed through that defect unharmed. The byte-exact assertions compare the parameter without its trailing padding, and then against the re-packed source schema rather than against a literal, so they pin the `Length` field and the payload octets without encoding a padding rule that #651/#664 is concurrently changing. Verified against a `git merge-tree` of this branch and #664: both library files auto-merge with no conflict, and all five new tests pass against the merged library. Two cases are deliberately accepted rather than rejected, and now say so in `_make_puzzle_field_width`'s docstring: `rhash_len == 0`, which is what the derived path yields for the default `random=0` and what a `Length = 4` parameter parses back to; and a `version` other than 1, which is treated as HIPv2 exactly as both readers' `version == 1` guards do. Coverage holds at 100% statement and branch on all three changed modules, with statements 1385 -> 1414 and branches 316 -> 338; 30 tests / 434 subtests -> 35 / 455. Scoped unit tier green: 291 passed, 1191 subtests. No EXPECTED_FAILURES entry moved -- still 45, with the same four HIP entries. No changelog entry on this branch: that is consolidated in #657.
JarryShaw
added a commit
that referenced
this pull request
Sep 22, 2026
…a false packaging claim (#642) (#666) Three names appeared in string annotations that their module never imported, and they were mypy's complete set of ``name-defined`` findings for the package: * ``pcapkit/utilities/logging.py:350`` used ``Any``; added to the ``TYPE_CHECKING`` block beside ``IO``, ``Optional`` and ``Union``. * ``pcapkit/protocols/schema/internet/ipv6_route.py:136`` used ``Protocol`` and ``:271`` used ``Optional``; added, ``Protocol`` as ``ProtocolBase as Protocol``. Twenty-one sibling schema modules already spell it that way, in the same ``payload:`` stub; this makes twenty-two. mypy 2.3.1 over ``pcapkit``: 3 ``name-defined`` errors before, 0 after; 115 total errors before, 112 after, so nothing else moved. ``MANIFEST.in:14-17`` asserted that ``include README.md`` was "the only thing that puts it in an sdist" and that an sdist without it "cannot be installed at all". Both halves are false, and the file had contradicted itself since #631 wrote the correct mechanism seven lines below without correcting this. Deleting the lines and rebuilding gives a byte-identical sdist listing -- empty diff -- that installs with exit 0: ``setuptools/command/sdist.py:59-60`` ships the README unconditionally and ``setuptools/dist.py:460``'s default ``license_files`` glob ships ``LICENSE``. Of the original three ``include`` lines only ``CHANGELOG.md`` is load-bearing. The comment now says that. New ``tests/project/test_annotation_names.py`` resolves every string annotation in the package -- following a nested forward reference such as ``'list["Nested"]'``, while treating ``Literal`` members and ``Annotated`` metadata as the values they are -- against the names its own module binds. It reports the same three findings as mypy on the unfixed tree and none after. That module named ``ast.TypeAlias`` and ``ast.TypeVar`` directly, and both are PEP 695 nodes added in Python 3.12, so *every* test in it raised ``AttributeError`` on the 3.10 and 3.11 matrix jobs -- ``bound_names`` walks every node of every file, so the attribute is reached whatever a test does. Both are now resolved once at module scope through ``getattr(ast, ..., ())``, leaving the ``isinstance`` branches otherwise untouched: ``isinstance(x, ())`` is always False, so the branches stay live on 3.12+ and are simply unreachable below it. Chosen over a ``sys.version_info`` comparison because it writes no version number down at all -- a comparison states 3.12 next to the attribute it guards, and the two can then drift -- and over a per-node ``getattr`` because a module-level constant lifts the lookup out of a loop that runs on every node of every file. ``ast.TypeVar`` is the branch that earns its keep: it carries its name as a bare ``str`` and emits no ``ast.Name`` node, so forcing ``_TYPE_VAR`` to ``()`` on 3.14.7 turns ``T`` and ``U`` into false findings. ``ast.TypeAlias`` is defensive by comparison -- its name *is* an ``ast.Name`` in ``Store`` context, which the preceding branch already catches -- and is left as it stands rather than removed. A new ``test_a_pep695_type_parameter_is_in_scope`` pins both the guards and the behaviour, skipped below 3.12 because its fixture source cannot parse there. Measured on real interpreters rather than simulated. 3.10.21 and 3.11.15: 5 failed, exit 1 -> 5 passed, 1 skipped, exit 0. 3.14.7: all 6 pass, exit 0. 133 passed over ``tests/project`` and ``tests/utilities/test_logging.py``, exit 0, subtests unchanged at 487. No changelog entry on this branch. Per the rule that no code branch touches ``CHANGELOG.md`` or anything under ``docs/source/changelog/``, this change's entry -- and the wording correction the ``MANIFEST.in`` claim implies for the #619 entry, plus the missing ``(#570)`` and ``(#577)`` citations -- go to the shared changelog pull request #657 instead.
``SeekableReader.truncate()`` now raises instead of returning a size, and the misspelled ``writeable()`` is spelled ``writable()``. Filed as **Changed** rather than **Fixed**, following the #617 entry: both halves are breaks to a public API, even though nothing inside the package called either method. CHANGELOG.md regenerated with ``util/changelog_md.py``; ``--check`` exits 0.
`register_protocol` keyed the protocol registry on `cls.__name__.upper()` and three dispatchable classes are named `HTTP`, so registering one silently displaced another. The entry records the measurement, the correction to the issue's `RegistryWarning` claim, why the guard departs from the presence-only siblings, and why re-keying belongs to #514. Regenerated CHANGELOG.md with util/changelog_md.py; --check exits 0.
Every PCAP-NG packet block lost its captured octets: they were extracted from the block schema and then overwritten by `ProtocolBase.__init__` with `self.packet.payload`, which the inherited `packet` had split at `PCAPNG.length` -- the wire's Block Total Length. The entry records the measurement on the committed `dhcp.pcapng`, the three affected block types and their three payload offsets, why the fix belongs at `PCAPNG.packet` rather than at the injection site, the 104-octet dump that made it a wire-format defect, why it ships labelled breaking, and that #678 is measurably unaffected. CHANGELOG.md regenerated with `util/changelog_md.py`; `--check` exits 0.
…ether literals The counts in `dbcd0e9aa` were written before the tests were hardened: the file grows 4 tests to 11 and 3 subtests to 24, not to 10 and 19. The entry now also records the two shapes no fixture exercised -- an option area after the captured data, which every block of `dhcp.pcapng` lacks, and a big-endian section -- and the two ways a snapped block is expressed, since those are what make the "every affected block type" claim more than the one type a fixture happens to have. Six inline literals also lost the punctuation that was attached to them: `` ``PCAPNG.PACKET_TYPES`` , `` and five like it, plus `` ``struct`` -only ``, from a line-wrapper that tokenised the paragraph without preserving adjacency. Reflowed with one that does, and with the check the generator already enforces -- a literal may not span a line break -- asserted rather than discovered. CHANGELOG.md regenerated with `util/changelog_md.py`; `--check` exits 0.
…ts coverage claim The cross-review on a second model returned NEEDS CHANGES on three points and the entry now reflects the code as it actually ships: * `PCAPNG.packet` is a plain `property`, not the `cached_property` it overrides. Caching it made a second `unpack` on one instance hand back the first call's octets -- an invariant the pre-#646 code held, since it recomputed from the schema every call. The entry records why, and the unit test now asserts it. * The coverage claim compared the base tree under the *old* tests against the branch under the new ones, which moves the suite and the library together and then credits the difference to either. Re-measured with the same tests on both: `misc/pcapng.py` 99.91% with its miss flat at 1, and `protocol.py` identical in every column rather than "232 to 228 misses", which is the check that its change really is docstring-only. * The #678 parity claim quoted a tally without saying what "101 truncation levels" meant; three readings of it give three different tallies. The set is now spelled as the expression that produced it, and the per-level fingerprint is given, since a matching aggregate can hide two levels that swapped. CHANGELOG.md regenerated with `util/changelog_md.py`; `--check` exits 0.
5 tasks
Records the three mis-parenthesised HTTP/2 payload length callbacks: the `else` arm returned `0` instead of subtracting `0`, so an unpadded DATA, HEADERS or PUSH_PROMISE frame parsed with its whole payload discarded. Carries the measured octets rather than a description, the algebraic reason padded frames could not have moved, the derivation that the padded arm has no off-by-one, the three plain-form sibling sites that localise the defect to the grouping, the four wrong fixes the new tests reject, and the `EXPECTED_FAILURES` and coverage numbers either side. Also notes the stale `:572` reference in the `httpv2-frame/PRIORITY` entry, whose guard is now at `:562`. `CHANGELOG.md` regenerated with `util/changelog_md.py`; `--check` exits 0.
12 tasks
… its counts A cross-review on a second model turned up two things the first #668 entry did not say, one of them a second half to the defect: - `BytesField` consults its `length` callback when packing as well as parsing, so the mis-parenthesised arm also declined to *write* the payload. An unpadded DATA frame packed to nine octets of header declaring 21, which is pcapkit emitting malformed HTTP/2 rather than only mis-reading it. The entry framed the defect as parse-side, following the issue; it now carries both halves and the measured octet counts for all three frames. - The first revision's tests were satisfied by a `max(computed, 1)` wrong fix on the two `fragment` fields, because every fixture carried a non-empty payload and only DATA had a padding-only case. Recorded as the sixth rejected variant, with what it silently produced. Counts corrected accordingly: 15 tests and 9 subtests to 23 and 12, four failures before to seven, 137/547 to 145/550, and 73→88/399→408 to 73→96/399→411. Also records the AST sweep of all 496 package files, and why the one other site sharing the shape (`ipv4.py:336`) is correct rather than the same defect. `CHANGELOG.md` regenerated with `util/changelog_md.py`; `--check` exits 0.
…to 14 The sweep leaves 1,495 of 1,509 levels parsing, so 14 still raise, not 13: 8 StreamEOFError, 4 FormatError and 2 ProtocolError. The per-type figures in the same sentence already summed to 14; only the total was wrong.
…ts sweep claim - The journal export block's bare struct.error, which #699 now fixes too: the block's own 32-bit NUL padding was read as a binary field's name, so every entry of unaligned length raised. Reachable from valid input. - The "no foreign exception" claim scoped to the truncation sweep, with the two families a fuzz still reaches named and measured unchanged either side: #701 (an unassigned block type raising from aenum) and #593's 32-bit band. - #704, the silent loss of every journal field after a binary one, noted as filed rather than fixed. - Where the two remaining ProtocolError levels actually cut: the Interface Description Block's if_tsresol option, not the Section Header Block. `python util/changelog_md.py --check` exits 0.
…fixed in #699 The cross-review's second pass found an OverflowError from a binary field's 64-bit length reaching BytesIO.read at 2**63 and above, and a UnicodeDecodeError from a field name, key or value that is not UTF-8. Both pre-existing, both in the function #699 had just fixed the struct.error in, both now clamped or replaced and reported. `python util/changelog_md.py --check` exits 0.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR is deliberately long-lived. Please do not merge it yet.
It is the shared changelog for the 1.5.0 cycle. Code pull requests in this cycle
ship without their own changelog entry, and their entry is committed here instead.
It stays open and keeps accumulating commits as more code PRs land, and it merges
last, once the code PRs are settled.
If you have arrived here wondering whether it is stalled: it is not. An open,
growing diff is the intended steady state.
Why it exists
Five open PRs each added one bullet to
docs/source/changelog/1.5.0.rstat the sameanchor and regenerated
CHANGELOG.mdbeside it. That made them mutually exclusiverather than independent — whichever merged first moved the anchor, and the other four
immediately re-conflicted on the two changelog files, even though no code file of
theirs overlapped at all. That cycle had already cost this wave thirteen rebases.
Separating the changelog from the code breaks the cycle: the code PRs stop contending
for a shared file, and the contention is concentrated in one place — here — where it
is a sequence of appends rather than a conflict.
What it currently covers
One commit per code PR, so it is obvious what is and is not accounted for:
TCP.readseeding its flag accumulator with atyping.castno-opFrame.len/cap_lenswapped between the PCAP and PCAP-NG readersExtractorclosing the caller's stream and leaking its ownextract(..., no_eof=True)never returningEvery bullet is the verbatim block its own PR added — not reworded, not reflowed,
not trimmed, and not reordered internally. The entry-file diff is a single hunk of 197
added lines with zero deletions, which is the mechanical guarantee of that.
Two deliberate choices
It is not squashed, and should not be. The one-commit-per-PR convention does not
fit a PR that accumulates over days. A commit per code PR keeps it reviewable and
makes coverage self-evident. Please do not "tidy" it into a single commit.
The first commit is a fix, not an entry.
2c4212a02regeneratesCHANGELOG.md,which has been drifted since
375e9d411(#638) hand-inserted three lines into thegenerated file instead of running
util/changelog_md.py. That left a stale copy ofthe #630 bullet sitting below its own correction, and stranded the #631 bullet after
#638.
That drift is why
mainis currently red, and it is unrelated to any of the five:python util/changelog_md.py --checkexits 1 onmain, failing theChangelog driftjob and all twelve matrix jobs, the latter through the same assertion in
tests/project/test_changelog_md.py::RepositoryStateTests. Measured on run35748204467(a62aed134): 13 of 14 jobs failed.It is kept as the first commit specifically so it can be cherry-picked ahead of the
rest of this branch if you want
maingreen before this PR merges:python util/changelog_md.py --checkexits 0 at every one of the six commits here.Still to come on this branch
detail below — which is tracked separately and lands here as its own commit.