Repository navigation
const(apptype): per-registry __canonical__ divergence makes get(port, proto=<multi-bit>) return a different service #759
Description
Activity
- addedbugIssues reporting a defect (set by the bug report template; a default, not an assessment)Issues reporting a defect (set by the bug report template; a default, not an assessment)constRegenerated IANA or vendor constant tables; members keep their numeric valuesRegenerated IANA or vendor constant tables; members keep their numeric valuesblockedDeferred pending another issue or decision; see the last comment for what unblocks itDeferred pending another issue or decision; see the last comment for what unblocks it
on Sep 24, 2026 Blocked on #769, which is open and regenerates all five files under
pcapkit/const/reg/apptype/plus thepcapkit/vendor/reg/apptype/apptype.pycrawler. This issue's fix lives in the generatedapptype.py, so working it now would collide with that regeneration and be overwritten by it.Unblocks when #769 merges. Re-measure before implementing: #769 changes the member-emitting template and the four range-row conditions #764 added, so the
__canonical__divergence may present differently — or already be resolved — on the regenerated tree.- removedblockedDeferred pending another issue or decision; see the last comment for what unblocks itDeferred pending another issue or decision; see the last comment for what unblocks it
on Sep 25, 2026 Unblocked — #769 merged as
83b58ebda, sopcapkit/const/reg/apptype/and the crawler are free.Re-measure before implementing rather than working from this issue as filed. #769 regenerated all five files: the per-member annotation is gone and every generator-emitted
TransportProtocol.get(...)is now attribute access, including the four range-row conditions #764 added. So the__canonical__divergence may present differently, or already be resolved, on the regenerated tree. The fix was also expected to be #732's "_dispatchrefuses a multi-bitproto", which is still deferred — check whether that is still the right shape before writing anything.- addedwipWork in flight - a covering PR is open or an agent is actively on itWork in flight - a covering PR is open or an agent is actively on it
on Sep 25, 2026 Re-measured on
83b58ebda, and this issue is ~46× larger than its body claims — but not a regression. I ran the sweep independently of the investigating agent; both agree on the count.multi-transport members swept : 10625 exceptions : 0 mints (member delta) : 0 service mismatches : 46 distinct ports : 20 ports: 42, 63, 80, 105, 351, 352, 666, 888, 999, 1525, 1701, 1989, 1992, 2049, 3000, 3002, 3478, 4444, 5349, 910046, not "exactly one at port 888." The body's number comes from a narrower lens than its own text describes: the 7 ports where
__canonical__disagrees between registries, filtered further to members that are multi-transport and their own registry's answer — true only at 888. A literal full sweep, which is what the body says it did, finds 46 and always has.Structure: 20 distinct ports, three of which carry two service names each (
3478→turn/stun-behavior,5349→turns/stun-behaviors, and one more), giving 23 (port, name) pairs, each contributing one TCP-declared and one UDP-declared copy: 17×2 + 3×4 = 46. Every mismatch carriesproto=tcp|udp; none involves sctp or dccp.AppType.__eq__compares on.portalone, soresult == mreadsTruein all 46 — the comparison cannot detect any of them.Not caused by #764 or #769. The identical sweep at
932cb48d1— this issue's own cited commit — gives the same 46 / 0 / 0, andtcp.py,udp.py,sctp.py,dccp.pyare byte-identical across932cb48d1..57b2c1761. #764 changed_missing_'s out-of-range propagation and per-transport range-row claiming, neither of which is reached here since no mismatch involves a mint; #769 changed only the annotation and theTransportProtocolreference form.Mechanism, confirmed by reading rather than inferred.
AppType._dispatch(pcapkit/const/reg/apptype/apptype.py:2332-2367) resolves a multi-bitprotoviashow_flag_values(proto), which iterates LSB-first (pcapkit/utilities/compat.py:184-197,_iter_bits_lsb).tcp = 1is the lowest bit, so anyprotocontaining it dispatches intoTCP's registry regardless of which registry the member being round-tripped belongs to.get()'s__canonical__lookup then returns that registry's single answer for the port.The fix is narrower and safer than expected. There is exactly one production call site of
AppType.get(port, proto=…)outside the const module —pcapkit/protocols/transport/transport.py:187,_make_port— and it always passes the concrete single-bit transport of the packet being parsed, never a member's own possibly-multi-bit.proto. So making_dispatchraise on a multi-bitprotobreaks no caller, converts 46 silent wrong answers into explicit errors, and stays the narrow half of #732 rather than the deferred Flag→Enum retype.Blocked on #777, which owns both
apptype.pyfiles. The fix belongs in theBASEtemplate inpcapkit/vendor/reg/apptype/apptype.pyfollowed by a regeneration, never a hand-edit of the generated file.- addedblockedDeferred pending another issue or decision; see the last comment for what unblocks itDeferred pending another issue or decision; see the last comment for what unblocks itand removedwipWork in flight - a covering PR is open or an agent is actively on itWork in flight - a covering PR is open or an agent is actively on it
on Sep 25, 2026 Unblocked — #777 merged at 12:16:37Z as
fe80b8525, sopcapkit/const/reg/apptype/apptype.pyandpcapkit/vendor/reg/apptype/apptype.pyare both free. Dispatching.Re-stating the fix so it is not re-derived:
_dispatchmust refuse a multi-bitprotorather than resolving it LSB-first. That is safe because exactly one production call site exists outside the const module —pcapkit/protocols/transport/transport.py:187in_make_port— and it always passes a single bit. Re-measured at83b58ebda: 46 mismatches at 20 ports, 0 exceptions, 0 mints across 10,625 members, identical to the count at932cb48d1, so this is long-standing rather than a regression.#775 now waits on this issue, reversing the earlier order: #775 regenerates all 121 registries including these two files, so the targeted fix lands first and the regeneration picks it up.
- addedwipWork in flight - a covering PR is open or an agent is actively on itWork in flight - a covering PR is open or an agent is actively on itand removedblockedDeferred pending another issue or decision; see the last comment for what unblocks itDeferred pending another issue or decision; see the last comment for what unblocks it
on Sep 25, 2026 3 remaining items
- removedwipWork in flight - a covering PR is open or an agent is actively on itWork in flight - a covering PR is open or an agent is actively on it
on Sep 25, 2026 - added 12 commits that reference this issue
on Sep 25, 2026
Metadata
Metadata
Assignees
Labels
Projects
- StatusShow more project fieldsDone
Describe the bug
__canonical__is per-registry and diverges between registries on 7 ports, soAppType.get(port, proto=<multi-bit>)can return a different service than the member it was asked to round-trip. It is silent, and==cannot detect it.Reproduction
On
932cb48d1, sweeping all 10,625 multi-transport members in one process — zero mints, zero exceptions, and exactly one service mismatch:__canonical__diverges on ports 113, 465, 512, 631, 750, 888, 999; 888 is the only one where the divergent member is multi-transport and is its own registry's answer.Expected behavior
get(m.port, proto=m.proto)should returnm's service, or refuse. It currently returns a different service and claims a narrower transport, and==reportsTruebecauseAppType.__eq__comparesself.port == other— so a caller checking equality sees no problem.System information
pcapkitVersion: checkout at932cb48d1Additional context
Found while verifying #736's acceptance criterion after #754 (#736 is otherwise closed — 10,624 of 10,625 round-trip correctly). Distinct mechanism from #736's displacement.
This is the case #732 already predicted. Its recorded note says the fix for multi-transport lookup is to make
_dispatchrefuse a multi-bitprotorather than silently pick LSB-first — that would turn this from a wrong answer into an error. Worth deciding alongside #732's deferredTransportProtocolretype rather than patching__canonical__per port.