diff --git a/WHATSNEW b/WHATSNEW
index bdad647e6ad..e76e4d1eeb1 100644
--- a/WHATSNEW
+++ b/WHATSNEW
@@ -428,6 +428,84 @@ Notable backward incompatible changes are the following:
The new parameter urgentOutOfBand, which LinuxTcp sets, gives the receive side of urgent data of Linux without SO_OOBINLINE: the last byte of urgent data leaves the stream, and a read stops before it. INET does not deliver that byte, which Linux gives to recv(MSG_OOB), and it sends no urgent data. Tcp keeps treating URG-marked bytes as ordinary data. Simulations change only where a peer sends urgent data to LinuxTcp.
+55. IEEE 802.11 Block Ack agreement lifecycle
+
+ HCF now dispatches accepted Block Ack Actions once after duplicate checks. Setup correlates the peer, TID, and dialog token. The recipient response determines the accepted buffer size and inactivity timeout. Both negotiated flags control A-MSDU use.
+
+ HCF defers new DATA extraction during setup and recovery. The first DATA under an accepted agreement retains the request sequence number. The response timeout starts after the request ACK, with a configurable default of 100 ms. Uncertain setup requires DELBA before surviving unsent DATA can resume. Failed DELBA discards the current deferred batch. Later DATA can start a fresh recovery exchange.
+
+ If an internal collision discards the first DATA before transmission, HCF retires the agreement and queues DELBA. The unsent successor remains deferred until recovery resolves. Lifetime expiry and RTS failure use the same retirement mechanism.
+
+ Directional teardown preserves the opposite agreement. The originator discards transmitted, unacknowledged DATA, even when its lifetime remains valid at inactivity expiration. It also discards unsent fragments through the highest transmitted allocation index. This local index records allocation order before fragmentation. This prevents transmission of an incomplete unit outside the agreement. Later intact DATA remains eligible, including across sequence wraparound.
+
+ Originator retirement now uses local allocation indices to preserve this boundary across large sequence gaps. A distance of exactly 2048 between DATA transmissions no longer lets unsent fragments survive teardown. The wire sequence numbers remain unchanged.
+
+ The recipient delivers complete buffered units in cyclic order across receive gaps before resource release. Association reset instead discards old buffered units in both directions. With Block Ack enabled, reset also discards queued and in-progress QoS DATA for the old peer, including unsent Normal Ack DATA. These changes can alter delivery, disposal, frame exchanges, and fingerprints.
+
+ Custom agreement handlers, recipient services, and management implementations require the changes in the migration guide. External queues need the optional selected-extraction contract only when deferred DATA hides eligible work. Built-in queues preserve ordinary pull accounting for that extraction.
+
+51. IEEE 802.11 Basic Block Ack transfer completion
+
+ Callers must supply agreement arguments explicitly for BAR selection and transmitted-BAR processing. An omitted argument now fails compilation instead of silently selecting behavior without agreement state.
+
+ Basic BAR selection now separates each peer and TID. Short bursts and small negotiated windows can cause BAR below the configured threshold. BAR can obtain channel access without queued DATA. After final completion or final DATA expiration, the originator sends DELBA even with zero inactivity timeout. Later bursts can require another ADDBA exchange. These changes affect exchange order, airtime, simulation results, and fingerprints for Basic Block Ack configurations.
+
+ Basic single protection now reserves only an already prepared successor. Further DATA extraction starts a new TXOP and can require more contention. Final DATA in a zero-limit TXOP reserves only its immediate response. These corrections can change Duration fields, airtime, and fingerprints.
+
+ Custom QoS ACK policies must accept the agreement-handler argument in isBlockAckReqNeeded() and computeBlockAckReqParameters(). Custom in-progress queues must preserve the extract argument in getPendingFrameFor(). QoS data services must implement IOriginatorQosMacDataService for candidate preview without extraction.
+
+52. IEEE 802.11 immediate Compressed Block Ack
+
+ HCF defaults to immediate Compressed Block Ack. Eligible HT infrastructure links and the supported ac path use accepted agreements and 32-byte Compressed responses with FCS. Links without usable local or peer HT state retain Normal Ack. Select blockAckVariant = "basic" at both endpoints for historical Basic compatibility. An HT PHY mode alone does not supply peer capability state.
+
+ Both endpoints must select the same blockAckVariant because ADDBA does not negotiate it. A valid addressed BAR with the other profile stops the simulation before agreement updates. The diagnostic identifies the peer, TID, and profiles. Compressed rejects delayedAckPolicySupported = true in either agreement policy at initialization.
+
+ Compressed receipt state retains positive bits after delivery and reports zero for absent receipts. HCF recognizes the required SIFS or RIFS relation from actual physical endpoints. It can release individual MPDUs under that relation despite a receive gap. Compressed agreements suppress fragmentation for their peer and TID; Basic retains its fragment bitmap.
+
+ Compressed BAR uses a legal non-HT rate. Initial and later BAR consume their applicable BSS and peer rate sets. A configured controlFrameBitrate must satisfy the applicable rule. Response duration, protection, and timeout calculations use the 32-byte response length. Basic retains its separate 152-byte response.
+
+ Active Compressed agreements check complete DATA exchange cost before destructive aggregation. If an aggregate cannot qualify, HCF evaluates its first ordinary MSDU with the same selected mode. Refusal preserves original queue frames, deadlines, and sequence state. Single protection reserves only the current exchange; a later BAR can require fresh contention. These changes can alter airtime, contention, delivery times, and simulation results. Basic and traffic outside active Compressed agreements retain their existing admission limits.
+
+ Before DATA commitment, failed RTS updates access-category retry state without a provisional sequence counter. Original DATA lifetimes bound repeated attempts. This changes the scoped path's former discard after a fixed number of RTS failures.
+
+ Existing demonstrations retain explicit Basic settings, configurations, and result rows. IEEE Std 802.11e-2005 introduced Basic; IEEE Std 802.11-2024 reserves its Type 0 value. Retention does not establish complete historical conformance. A-MPDU requirements for n and ac remain deferred. Multi-TID Block Ack and the existing delayed Basic requirement also remain deferred. Compressed support does not establish complete VHT transfer support or MAC stop/start recovery.
+
+ Custom QoS rate selectors, recipient ACK policies, recipient data services, and frame sequences require the interface changes in the migration guide. Custom radio media, communication caches, and IEEE 802.11 modes also require the new timing queries. Basic classes and methods remain available.
+
+53. Radio medium timing queries
+
+ Radio media now expose copied transmission endpoints and cached arrival endpoints. Consumers can preserve physical timing evidence after cache expiry without retained transmission pointers or new propagation calculations. Receivers attach the transmission identifier to SignalTimeInd so later frame delivery can identify that evidence. Custom radio media and communication caches must implement the new query methods described in the migration guide.
+
+54. IEEE 802.11 signal extension
+
+ HT duration at 2.4 GHz now includes its six-microsecond signal extension. Previously, that waveform ended early, which could change interframe recognition and airtime. Custom IEEE 802.11 modes must supply the new nominal-extension query. A transmission query supplies the actual extension from its waveform so consumers do not count it twice.
+
+55. IEEE 802.11 Block Ack wire types
+
+ The serializer supports the complete four-bit Type field for Basic compatibility and Compressed frames. Compressed responses contain a 64-bit bitmap and occupy 32 bytes with FCS. Both BAR profiles occupy 24 bytes with FCS. Basic producers must keep reserved bits that overlap Type zero. Invalid Types and malformed fragment fields now fail validation instead of ambiguous decoding.
+
+56. IEEE 802.11 RIFS permission
+
+ HT Operation now preserves RIFS Mode through management conversion and equality checks. The MIB permits RIFS under unprotected HT operation and clears the permission for VHT. A recipient can therefore check the actual advertised permission before it recognizes a RIFS relation. This change adds no RIFS transmission scheduler.
+
+57. IEEE 802.11 reversible DATA preparation
+
+ QoS data services now supply an owned DATA preview before queue extraction or sequence assignment. A successful commit transfers that same preview to the caller. A refused commit preserves the preview and original queue frames. Custom services must implement the preparation contract in the migration guide. The existing extraction path remains available.
+
+ Commitment of ordinary DATA now preserves normal queue service effects. Queue output statistics count the departure, and the retained preview includes elapsed queueing time and queue events. This also applies when a deferred head causes selection of a later eligible frame.
+
+58. IEEE 802.11 HT-SIG bytes and aggregate boundaries
+
+ HT transmissions now preserve the complete HT-SIG header through byte conversion. Receivers check its CRC, supported fields, and length before length-based trailer removal. Invalid headers mark packets as erroneous. Delimiter inspection supplies bounded MPDU ranges, including singleton aggregates. Ordinary valid HT duration inputs remain unchanged. VHT modes now create their own header type.
+
+59. IEEE 802.11 partial HT aggregate reception
+
+ NIST and Yans receivers now retain separate delimiter and MPDU outcomes for HT20 aggregates. Partial DATA loss preserves aggregate bytes for later MAC acceptance. This change does not enable MAC aggregate receipt or implicit Block Ack. Custom HT error models must implement the optional WLAN aggregate contract. Aggregate error-rate estimates can be unknown. The migration guide describes the contract and metadata changes.
+
+ Rx now drops aggregate containers before standalone FCS or MAC header checks. This prevents parser errors for successful and partially failed aggregates. PHY range outcomes remain available in the drop signal. MAC aggregate acceptance remains pending.
+
+ Delimiter recovery now scans across typed MPDU headers. An invalid delimiter no longer causes a chunk conversion error at those headers. Bounded recovery retains later complete MPDU ranges.
+
Notable backward compatible changes are the following:
1. IEEE 802.11 per-station rate statistics
@@ -504,12 +582,16 @@ Notable backward compatible changes are the following:
14. Explicit historical Basic Block Ack selection
- HCF adds blockAckVariant = "basic" for historical compatibility. Existing Block Ack, TXOP, QoS, and fragmentation demonstrations select it explicitly. Basic remains the default. HCF rejects unknown values and the reserved "compressed" value during initialization until Compressed exchanges exist. These checks also apply when Block Ack support is disabled. Basic originated in IEEE Std 802.11e-2005; IEEE Std 802.11-2024 reserves its type value.
+ HCF retains blockAckVariant = "basic" for historical compatibility. Existing Block Ack, TXOP, QoS, and fragmentation demonstrations select it explicitly. HCF rejects unknown selector values during initialization, also when Block Ack support is disabled. Basic originated in IEEE Std 802.11e-2005; IEEE Std 802.11-2024 reserves its type value.
15. IEEE 802.11 retry counters for each receiver
QoS recovery now keeps separate retry counters for each receiver. Frames with equal TID, sequence number, and fragment number no longer share a counter across receivers. An acknowledgment or retry-limit disposal for one receiver preserves the other receiver's counter. This repair applies before DATA lifetime expiration.
+16. Optional selected extraction from packet queues
+
+ PacketQueue, CompoundPacketQueueBase, and PriorityScheduler now support the optional ISelectivePacketCollection contract. A consumer can find an eligible packet without a state change. It can extract that packet with ordinary pull events, residence metadata, and ownership transfer. Ordinary head selection remains unchanged. Providers without this capability report UnsupportedPacketSelection before extraction.
+
Notable bug fixes are the following:
1. IEEE 802.11 HT modes preserve the requested band and preamble
diff --git a/doc/src/migration-guide/index.rst b/doc/src/migration-guide/index.rst
index 6da76178c57..1bc0127f048 100644
--- a/doc/src/migration-guide/index.rst
+++ b/doc/src/migration-guide/index.rst
@@ -14,6 +14,100 @@ retransmissions do (``synRetries``). When they run out, the connection raises th
``TCP_E_TIMEOUT_CONN_ESTAB``, as the timer did before, so a subclass that handles this event needs no
change.
+IEEE 802.11 HT A-MPDU PHY reception
+---------------------------------
+
+HT receivers require a present error model to implement ``IIeee80211AmpduErrorModel``. This optional WLAN contract provides pure mode-support queries and probabilities for bounded PSDU ranges. ``Ieee80211ErrorModelBase`` supplies the implementation for NIST and Yans. Custom HT models outside this base must implement the C++ contract and its NED module interface. Non-HT models require no aggregate role.
+
+An absent error model remains valid. The receiver retains its acquisition, sensitivity, attempt, and SNIR checks without error draws. This includes ordinary HT operation with ``Ieee80211UnitDiskRadio``. The aggregate profile covers scalar and dimensional HT20 reception; it adds no unit-disk aggregate support claim.
+
+The receiver checks role presence at physical-layer initialization. Before the MIB update, the MAC supplies its exact mode set, channel widths, and spatial-stream limit for receive coverage validation. The model must support the modes that these inputs advertise, including HT20 short GI. Unsupported modes outside the effective stream limit cause no rejection. Runtime mode-set changes also check role presence. Any later capability-update caller must validate its exact inputs before the MIB update.
+
+BerTable retains its current limit. Its NED type rejects the radio's HT operation string. The allowed ``n`` value reaches its initializer, which also rejects HT. Both failures precede the new role check. BerTable does not gain HT aggregate support. Supported HT configurations can use NIST or Yans.
+
+The new role diagnostic applies to otherwise valid custom HT models without the contract. The current packet-level BerTable reception path also requires a transmission bit model. Scalar packet-level transmissions do not supply that model. This existing limitation remains outside the HT A-MPDU change.
+
+The modeled HT-SIG header preserves all 48 bits through byte conversion. ``lengthField`` supplies HT Length. ``aggregation`` distinguishes an A-MPDU from a standalone MPDU, including singleton aggregates. The radio derives mode fields from the selected mode and calculates the CRC after field assignment. Decode preserves the received CRC. HEADER validation rejects invalid CRC and unsupported fields before delimiter or MPDU outcomes.
+
+Separate-part reception retains its current abort; whole-signal reception supplies a failed container. DATA separately refuses modeled content shorter than a valid HT Length. This refusal retains the accepted header property but supplies no range outcomes. It represents malformed model input, not physical carrier loss. Decapsulation never uses an invalid header or insufficient content for length-based trailer removal. Ordinary valid HT duration inputs remain unchanged.
+
+``Ieee80211AmpduInd`` carries copied PHY facts without reception or transmission pointers. Each range identifies a delimiter or complete MPDU by PSDU-relative byte offset, byte length, validity, and physical success. Spacing delimiters have four-byte ranges. Alignment padding has no range outcome or separate draw. MAC delimiter and FCS checks still control acceptance; the PHY indication alone grants no delivery or response permission.
+
+The default ``Rx`` rejects containers whose PHY indication identifies an aggregate before standalone FCS or MAC header checks. This applies even when every range succeeds. The drop signal retains the PHY outcomes. Custom receive modules must keep aggregate containers separate from standalone frame parsing until they implement member acceptance. MPDU deaggregation can extract bounded recovered members from an incomplete layout. Extraction alone grants no MAC acceptance.
+
+The medium cache retains each aggregate decision. PREAMBLE calculates no HEADER or DATA outcome. HEADER, DATA, WHOLE, and direct success queries reuse retained prerequisites and samples. WHOLE summarizes all ranges. Partial DATA failure preserves the aggregate bytes for the later MAC receive integration. This change alone does not enable MAC A-MPDU receipt or implicit Block Ack responses.
+
+With a present model, aggregate ``ErrorRateInd`` fields contain ``NaN`` for unknown probabilities. The receiver does not calculate the former whole-PSDU estimate for these fields. Member outcomes are separate from probability estimates. Without a model, the receiver preserves the common base's tag presence and values. Ordinary MPDUs retain their current metadata. Custom readers of aggregate error-rate fields must accept unknown values.
+
+NIST and Yans calculate each range probability from its byte length and the configured scalar SNIR reduction. The receive profile includes HT20 long GI and short GI. Each range uses an independent draw when its success probability lies strictly between zero and one. This model does not represent correlated FEC errors or interference changes within one member. Alignment padding contributes duration but receives no separate outcome. The model checks establish no new error-accuracy or goodput claim.
+
+Radio medium timing queries
+--------------------------
+
+Custom radio media must implement ``getTransmissionTimingSnapshot()`` and ``computePrecedingSignalGap()``. These queries supply copied physical endpoints from actual transmissions and cached arrivals. Unavailable endpoints produce an absent result. The snapshot must not retain transmission, arrival, or radio pointers. Communication caches must implement the const ``findCachedArrival()`` query without insertion or other mutation.
+
+``SignalTimeInd`` now carries the medium's transmission identifier. The receiver supplies this local indication when it accepts a transmission. Consumers can associate later frame delivery with the corresponding physical timing evidence. The identifier is local metadata; it adds no transmitted field.
+
+IEEE 802.11 signal extension
+---------------------------
+
+Custom IEEE 802.11 modes must implement ``getSignalExtensionTime()``. ``Ieee80211ModeBase`` supplies zero. ERP and 2.4 GHz HT modes supply their six-microsecond extension. HT duration now includes that extension, which can change airtime and simulation results. The transmission query derives the actual extension from its waveform, so consumers must not add the nominal value again.
+
+IEEE 802.11 RIFS permission
+--------------------------
+
+HT Operation now preserves RIFS Mode through management conversion and equality checks. VHT operation clears that permission. The change preserves the received permission for a consumer that checks RIFS relations; it adds no RIFS transmission scheduler.
+
+IEEE 802.11 Block Ack wire producers
+-----------------------------------
+
+The serializer decodes the complete four-bit Type field. Type 0 retains Basic compatibility; Type 2 selects Compressed. Other Types produce invalid input. Basic producers must keep the two low bits of their reserved field zero because those bits overlap Type. The serializer rejects inconsistent Basic objects. Compressed reserved fields write zero; received reserved values do not affect the decoded result.
+
+Compressed producers must supply Fragment Number zero and a 64-bit bitmap. A Compressed Block Ack header chunk contains 28 bytes before FCS. Both BAR profiles occupy 24 bytes with FCS. Keep the corresponding header chunk and trailer lengths consistent. The serializer rejects malformed wire fields before runtime processing.
+
+IEEE 802.11 reversible DATA preparation
+-------------------------------------
+
+Custom QoS data services must implement the reversible preparation operations in ``IOriginatorQosMacDataService``. ``prepareFrame()`` creates an owned preview and retains original MSDUs in their queue. ``isPreparationValid()`` checks current source identity, eligibility, lifetime, sequence state, and aggregation permission. ``commitPreparedFrame()`` transfers the same preview pointer only after successful validation. Failure must preserve the preview and all original queue frames. ``InProgressFrames::adoptPreparedFrame()`` accepts a committed frame without another extraction.
+
+IEEE 802.11 Compressed Block Ack extension points
+------------------------------------------------
+
+HCF defaults to ``mac.hcf.blockAckVariant = "compressed"``. Both endpoints must select the same profile. ADDBA does not negotiate this selector. A valid addressed BAR with the other profile stops the simulation before agreement updates. The diagnostic identifies the peer, TID, local profile, and received profile. Configure the same selectors before a new run.
+
+Compressed agreements require usable local HT support and directional peer HT state. An HT PHY mode alone does not supply that state. Without it, HCF retains Normal Ack DATA. Explicit ``blockAckVariant = "basic"`` retains historical Basic compatibility without that HT requirement. Compressed agreements suppress fragmentation for their peer and TID. Fragmentation outside those agreements retains its existing threshold.
+
+Compressed supports immediate Block Ack only. Keep ``delayedAckPolicySupported = false`` in both agreement policies. HCF rejects either true value during initialization when Compressed support is enabled. Basic retains its existing delayed-policy configuration contract.
+
+Custom QoS rate selectors must implement ``computeCompressedBlockAckReqMode(packet, header, startsTxop)``. HCF supplies the request's actual position in the TXOP. Select a legal non-HT mode from the applicable BSS or peer rates. A configured ``controlFrameBitrate`` must satisfy that rule. The existing Basic rate-selection methods remain available.
+
+Custom recipient ACK policies must implement ``computeCompressedBlockAckDurationField()``. This operation subtracts SIFS and the actual Compressed response airtime from the request reservation. Compressed Block Ack contains 32 bytes with FCS; Basic contains 152 bytes. Retain the existing Basic operation.
+
+Custom recipient data services must accept ``bool interframe = false`` in ``dataFrameReceived()``. The value states whether HCF established the required SIFS or RIFS relation. A false value supplies no interval evidence. Preserve that value through the recipient reorder path. Custom callers that omit it retain false.
+
+Custom frame sequences must supply HCF's resolved profile to ``QoSContext``. Its constructor retains Basic for callers that omit the profile. Custom agreement creation must set the recipient agreement's variant before use. An agreement must retain that variant throughout its lifetime.
+
+Custom frame sequences must preserve BAR arbitration before DATA preparation. The HCF preparation callback distinguishes the legacy path, accepted preparation, and admission refusal. Refusal must end the sequence without a DATA failure. Keep the selected DATA mode through RTS/CTS. Validate that decision without another adaptive rate query. Admit the complete exchange before source removal.
+
+The active Compressed path reserves only the current exchange under single protection. A later BAR can require fresh contention. Before DATA commitment, failed RTS updates QSRC and the contention window without a provisional sequence counter. QSRC is the short retry counter for an access category. QSRC rollover does not discard queued DATA. Original lifetimes bound repeated RTS attempts.
+
+Basic and outside-agreement fallback retain their existing preparation and recovery paths.
+
+IEEE 802.11 Basic Block Ack extension points
+------------------------------------------
+
+HCF previews the next eligible DATA before BAR selection. Preview must preserve queue ownership and the next sequence number. Custom QoS data services must implement ``IOriginatorQosMacDataService``. This contract extends ``IOriginatorMacDataService`` with ``peekEligibleFrame()``, ``getNextSequenceNumber()``, and ``hasAMsduAggregation()``.
+
+Basic single protection reserves only an already prepared successor. HCF ends the TXOP before further DATA extraction. A queued unit can change size or require agreement setup before transmission. Custom frame sequences must preserve this boundary when they use the same forecast.
+
+Custom QoS ACK policies must add ``IOriginatorBlockAckAgreementHandler *agreements`` to ``isBlockAckReqNeeded()`` and ``computeBlockAckReqParameters()``. The argument supplies current agreement windows. Policies must keep peer and TID identities separate. The supplied policy can request BAR without a DATA candidate.
+
+Callers must supply the agreement handler explicitly through both the interface and concrete policy types. An omitted argument fails compilation. Pass ``nullptr`` explicitly when no agreement handler is available; the supplied policy then requests no BAR. Custom overrides must also require the argument.
+
+Calls to ``QosAckHandler::processTransmittedBlockAckReq()`` must supply the originator agreement explicitly. The agreement restricts processing to the negotiated window. An explicit ``nullptr`` retains bitmap-only coverage without that restriction. Production HCF always supplies the active agreement.
+
+Custom ``InProgressFrames`` classes must add ``bool extract = true`` to their ``getPendingFrameFor()`` override. When ``extract`` is false, the method must return a borrowed candidate without extraction or sequence assignment. The default value preserves ordinary extraction. Custom candidate sources must also implement ``peekFrameToTransmit()`` without extraction.
+
TCP Algorithms
--------------
@@ -158,6 +252,51 @@ The protected ``getEarliestCompleteMsduOrAMsduIfExists()`` helper remains availa
No configuration change is necessary. Gap release, window movement, and BAR before DATA can change delivery times and fingerprints in affected Basic Block Ack scenarios. These repairs do not enable Compressed Block Ack.
+IEEE 802.11 Block Ack Agreement Lifecycle
+------------------------------------------
+
+HCF now defers new DATA extraction for a peer and TID during Block Ack setup or recovery. Already numbered DATA resolves before HCF creates ADDBA. The first DATA under an accepted agreement therefore retains the request sequence number. Both supported profiles use this lifecycle contract.
+
+The recipient response now determines the buffer size and inactivity timeout. The built-in recipient selects the smaller of 64 and its configured maximum. A request size of zero supplies no size guidance. The response copies the request dialog token. Both negotiated A-MSDU flags must permit aggregation under an active agreement.
+
+The originator starts ``mac.hcf.originatorBlockAckAgreementPolicy.addbaFailureTimeout`` after the request receives its ACK. Its default is 100 ms. The value must be positive and finite.
+
+If the response does not arrive, HCF sends DELBA before it releases surviving unsent DATA. DATA lifetime separately limits retained DATA. Failed DELBA disposes of the current deferred batch. Later DATA starts one fresh recovery exchange.
+
+At ordinary teardown, the originator discards every transmitted, unacknowledged MPDU for the selected agreement. This includes DATA whose lifetime remains valid when inactivity expires. It also discards unsent fragments through the highest transmitted allocation index, even if an earlier fragment received an ACK. This local index records allocation order before fragmentation. Later unsent DATA retains sequence continuity.
+
+For example, fragment 0 of sequence 4095 enters transmission before teardown. Teardown discards its unsent fragments and preserves the intact successor with sequence 0. The recipient delivers complete buffered units in cyclic order before it releases resources. It discards incomplete units. For example, complete units 101 and 102 can reach the upper process after teardown across absent unit 100.
+
+Custom originator code must call ``recordTransmittedData(allocationIndex)`` instead of ``markFirstDataTransmitted()`` or the former cyclic-sequence overload. The QoS data service assigns an increasing local index before fragmentation. All fragments retain this index in ``Ieee80211DataSequenceTag``. Custom QoS data services must supply the same metadata. The agreement exposes its highest transmitted index through ``getHighestTransmittedDataIndex()``, which replaces ``getHighestTransmittedSequence()``. HCF records this index when the transmitter accepts DATA.
+
+The index remains available after ACK processing removes a frame. Teardown compares allocation indices to remove unsent fragments of a partially transmitted unit. This comparison remains valid across sequence gaps and wraparound. For example, transmission of sequences 0 and 2048 updates the boundary correctly when intervening DATA receives allocation indices but never transmits. Retransmission of older DATA cannot move the boundary backward.
+
+Association reset instead discards all buffered units from the old association. Management calls ``IBlockAckAgreementLifecycle::resetPeerAgreements()`` with the old peer address before replacement. The MAC implements this contract and forwards the reset to HCF. Custom management implementations must supply this handoff for association replacement and loss. A rate-state notification alone cannot identify replacement with identical capabilities.
+
+With Block Ack support enabled, peer reset also discards queued and in-progress QoS DATA addressed to the old peer. This includes unsent Normal Ack DATA without an agreement. Accepted physical exchanges retain their context until safe completion. Reset preserves other peers' queued DATA and lifetime records.
+
+Custom MAC implementations that use these management modules must implement ``IBlockAckAgreementLifecycle``. The reset must complete both agreement roles' cleanup before it reports deletion. It must preserve an unrelated management exchange that already holds an accepted transmission.
+
+``blockAckAgreementDeleted`` now supplies a nonnull agreement after removal from lookup and completion of the required cleanup. The payload identifies the selected role through its agreement class. The payload remains valid only during the synchronous callback. Listeners must not retain or modify it. Association reset removes both roles before the first callback. Ordinary directional teardown preserves the opposite role.
+
+Custom QoS recipient services must return accepted, complete Actions from ``managementFrameReceived()``. HCF owns their agreement dispatch after duplicate checks. Services must implement directional retirement and peer cleanup through the recipient-service contract. Custom agreement policies must implement ``isProfileAllowed()`` with the supplied typed profile and usable capability inputs. Basic admission must preserve its historical non-HT compatibility.
+
+Custom agreement handlers must implement the new procedure and retirement operations in their handler contracts. The originator handler owns setup intent, request identity, response deadlines, teardown, and recovery state. Its ``noteEligibleDataTransmission(peer, tid)`` operation records ``WAITING_FOR_SUCCESSOR`` after an eligible DATA transmission. This phase permits ordinary DATA access and has no request or deadline. A later queued successor promotes the phase to setup intent before extraction. Peer reset clears the phase.
+
+The recipient handler owns its active agreements and pending teardown. Detachment removes an agreement from lookup and transfers it to the caller. The caller retains the object through its deletion callback. Const observations expose current state without a second agreement store.
+
+Custom implementations of ``IBlockAckAgreementHandlerCallback`` must implement ``scheduleInactivityTimer()`` without a deadline argument. Agreement handlers request recomputation after their state changes. HCF queries both roles and schedules their earliest absolute deadline. It cancels the timer when neither role has a finite deadline. Custom callers must remove the old deadline argument.
+
+Custom subclasses of ``OriginatorQosMacDataService`` must adapt the protected ``aMsduAggregateIfNeeded(queue, selected)`` helper. The selected packet is the first eligible packet in service order. An aggregate candidate list must start with that packet. The service validates every candidate before source removal and calls the aggregation policy once.
+
+Custom queues need ``inet::queueing::ISelectivePacketCollection`` only when a deferred next frame hides eligible work. HCF retains ordinary dequeue when the advertised next frame is eligible. It waits when every frame is deferred. It reports the missing capability before mutation when eligible work requires a bypass.
+
+The optional ``peekEligiblePacket(predicate)`` operation returns a borrowed packet in configured service order without mutation. ``extractPacket(packet)`` transfers that exact packet to its caller. Implementations must preserve their ordinary pull effects, including ownership, residence metadata, events, statistics, and applicable animation. Built-in simple queues, priority schedulers, and compound queues support this contract. Custom subclasses that change pull effects must adapt selected extraction. The optional contract supports complete packets only.
+
+Providers that cannot perform a selected operation must throw ``inet::queueing::UnsupportedPacketSelection``. This exception permits ordinary service and collection inspection without the optional capability. Genuine queue and predicate errors must retain their original type.
+
+External frame-sequence handlers must implement ``interruptFrameSequence()``. The handler owns the interruption outcome after recipient processing. It suppresses failure feedback when the transmitted DATA or management frame no longer belongs to the in-progress queue. Other exchanges retain ordinary failure feedback. Cleanup must destroy the exchange context before it deletes deferred frame copies. These repairs do not add MAC stop/start recovery or final-transfer teardown.
+
IEEE 802.11 Response Query Roles
--------------------------------
diff --git a/doc/src/users-guide/ch-80211.rst b/doc/src/users-guide/ch-80211.rst
index f4569882f96..fa35d64e63b 100644
--- a/doc/src/users-guide/ch-80211.rst
+++ b/doc/src/users-guide/ch-80211.rst
@@ -533,7 +533,7 @@ The first exchange proceeds even when its cost exceeds a positive TXOP limit. Th
A zero limit permits the initial exchange and its existing RTS/CTS and immediate response. Each later fragment or unrelated unit needs a fresh TXOP. Prepared fragments and A-MSDUs use the same complete-cost check under positive limits.
-``isBlockAckSupported = true`` disables complete-cost continuation admission, even without a Block Ack agreement. Multiple protection also retains its separate admission policy. Both paths use shared TXNAV. Basic Block Ack admission, BAR forecasts, zero-limit companion rules, and optional overrun exceptions remain outside this repair.
+With Block Ack support enabled, active Compressed agreements use the admission described in :ref:`ug:sec:80211:blockack`. Basic and traffic outside those agreements retain their existing admission policy. Those retained paths can exceed DATA exchange bounds. All paths use shared TXNAV. Basic Block Ack admission and BAR forecasts remain outside the Compressed correction.
All single protection paths classify group frames before the current-frame ACK equation. With Block Ack support disabled, the current one-frame group sequence advertises zero. With support enabled, its legacy forecast can reserve a queued successor that the sequence cannot transmit. This limit applies to group management and non-QoS DATA too.
@@ -607,13 +607,9 @@ The standard separately requires AIFSN of at least 1 for APs and at least 2 for
example, a non-AP station with AIFSN 1 meets the ownership condition but violates the standard's
minimum.
-Multiple protection does not check a complete exchange against the remaining budget.
-For example, its admission policy can start a 120 us exchange with only
-100 us left. The reference scope assumes whole-microsecond positive limits, integral initial PPDU
-airtime, and integral legacy RTS airtime. It adds no A-MPDU, Compressed Block Ack, new
-fragmentation, CTS-to-self, dual CTS, PIFS recovery, or TXOP sharing. It also adds no reverse
-direction, beamforming, S1G, multi-user, or general MAC lifecycle support. It does not repair
-general rate selection, recipient response timing, or TXOP overruns.
+Outside active Compressed agreements, multiple protection does not check complete exchange cost against the remaining budget. For example, that retained policy can start a 120 us exchange with only 100 us left. Active Compressed agreements use their separate complete-cost admission. The reference scope assumes whole-microsecond positive limits, integral initial PPDU airtime, and integral legacy RTS airtime.
+
+Multiple protection adds no A-MPDU, new fragmentation, CTS-to-self, dual CTS, PIFS recovery, or TXOP sharing. Reverse direction, beamforming, S1G, multi-user operation, and general MAC lifecycle support remain outside this implementation. General rate selection and recipient response timing retain their separate limits.
.. _ug:sec:80211:fragmentation:
@@ -700,16 +696,35 @@ station then sets up an agreement with its peer automatically (with ADDBA
frames) when it sends a QoS data frame shorter than
:par:`maxBlockAckPolicyFrameLength` (default 1000 bytes). After that, the
frames of that traffic class go out without an immediate acknowledgement,
-and the station sends a Block Ack Request after :par:`blockAckReqThreshold`
-(default 5) unacknowledged frames. Both parameters are on
-``mac.hcf.originatorAckPolicy``.
+and the station normally sends BAR after :par:`blockAckReqThreshold` unacknowledged frames. The default threshold is five. Both parameters are on ``mac.hcf.originatorAckPolicy``.
+
+The station applies the threshold separately to each peer and TID. It sends BAR below the threshold when the next eligible DATA belongs to another agreement or cannot fit the transmit window. The station also sends BAR when no eligible DATA remains. For example, a burst of four frames ends with BAR even with the default threshold of five. A negotiated buffer size of one requires BAR before the second sequence number.
+
+BAR can obtain channel access without a queued DATA frame. A later BAR within a TXOP must fit the applicable TXOP and TXNAV bounds with its response. If BAR cannot fit, the station retains that work for another contention.
+
+After the final exchange, the originator sends DELBA when its agreement has no queued DATA, in-progress DATA, or active BAR exchange. A zero inactivity timeout does not disable this transfer-completion rule. DATA that arrives after this boundary waits for DELBA completion. A later transfer can require another ADDBA exchange. If the last unresolved DATA expires, the originator uses the same teardown path. Ordinary recipient teardown releases complete units behind a receive gap before it releases agreement resources.
+
+The agreement policies, ``mac.hcf.originatorBlockAckAgreementPolicy`` and ``mac.hcf.recipientBlockAckAgreementPolicy``, set the buffer size. ``maximumAllowedBufferSize`` defaults to 64. Both negotiated ``aMsduSupported`` flags control A-MSDU use; each defaults to ``true``. ``blockAckTimeoutValue`` defaults to ``0s``, which disables the inactivity timeout. Transfer completion and DATA lifetime expiry retain their separate teardown rules.
+
+HCF defaults to ``mac.hcf.blockAckVariant = "compressed"`` for immediate Compressed Block Ack. Block Ack support itself remains disabled by default. The response contains a 64-bit bitmap and occupies 32 bytes with FCS. Each positive bit acknowledges one unfragmented MPDU. The recipient retains receipt bits after delivery, so a later BAR can still obtain those positive bits. Missing receipt evidence produces zero.
+
+Compressed agreements require usable local HT support and directional peer HT state. Infrastructure association supplies that peer state for eligible HT links and the supported ``ac`` path. An HT radio mode alone does not supply peer capabilities. Without usable state, DATA uses Normal Ack. Local non-HT operation produces a warning when Block Ack support is enabled. HT ad hoc links without peer state retain Normal Ack.
+
+Explicit ``blockAckVariant = "basic"`` selects historical Basic compatibility on both endpoints. Basic retains its 64 fragment bitmap entries and its 152-byte response with FCS. IEEE Std 802.11e-2005 introduced this format. IEEE Std 802.11-2024 reserves its Type 0 value. INET retains Basic for existing studies; this compatibility does not establish complete historical conformance.
+
+Both endpoints must select the same profile because ADDBA does not negotiate it. A valid addressed BAR with the other profile stops the simulation before agreement updates. The diagnostic identifies the peer, TID, and profiles. Configure matching profiles before a new run. Malformed frames follow input rejection instead of this configuration diagnostic.
+
+Compressed agreements suppress fragmentation for their peer and TID. The recipient rejects fragmented DATA under those agreements before receipt or reorder updates. Basic retains fragmentation. Outside a Compressed agreement, the configured fragmentation threshold retains its existing effect.
+
+Keep ``delayedAckPolicySupported = false`` in both agreement policies for Compressed support. HCF rejects true at initialization because the Compressed path supplies immediate responses only. Retained Basic delayed-policy configuration does not establish a complete delayed exchange implementation.
+
+The Compressed originator sends BAR with a non-HT PHY mode. Initial BAR uses the applicable BSS basic or mandatory rate rule. Later BAR uses the peer operational rate rule. A configured ``controlFrameBitrate`` must satisfy that rule. The response selector uses the actual 32-byte response length when a legal alternate rate is available. These rules retain separate Basic, ACK, and CTS contracts.
+
+Active Compressed agreements check the complete DATA exchange before source commitment. If an aggregate cannot qualify, HCF evaluates its first ordinary MSDU with the same selected DATA mode. If neither form qualifies, HCF ends the sequence without queue removal, sequence assignment, or deadline replacement. RTS/CTS retains the selected mode. Expiry or capability changes before DATA cancel invalid preparation.
+
+An initial unaggregated MSDU can use the applicable TXOP overrun exception. A same-size single-MPDU retransmission can also use its exception. That retransmission can carry an A-MSDU. Each exception permits at most one Data or Management frame in that TXOP. A new A-MSDU must fit a positive limit. A zero limit permits one DATA PPDU with one MSDU or A-MSDU, plus its required protection and response.
-The agreement policies, ``mac.hcf.originatorBlockAckAgreementPolicy`` and
-``mac.hcf.recipientBlockAckAgreementPolicy``, set the buffer size
-(:par:`maximumAllowedBufferSize`, default 64), the A-MSDU support
-(:par:`aMsduSupported`, default ``true``) and the inactivity timeout
-(:par:`blockAckTimeoutValue`, default ``0s``, which means no timeout). Only
-the basic Block Ack variant is modeled.
+Single protection reserves only the current Compressed exchange. A later BAR can require fresh contention. This policy can change airtime and delivery times. Before DATA commitment, failed RTS updates QSRC and the contention window without a provisional sequence counter. QSRC is the short retry counter for an access category. Original MSDU lifetimes bound repeated attempts; QSRC rollover does not discard them.
.. code-block:: ini
@@ -1026,9 +1041,9 @@ The model does not contain:
- PCF and HCCA, the polled channel access methods. The module
:ned:`Hcca` exists as a placeholder.
-- A-MPDU aggregation, the MSDU lifetime limit, and self-CTS protection.
+- A-MPDU aggregation and self-CTS protection.
-- Compressed and multi-TID block acknowledgement.
+- Multi-TID block acknowledgement and delayed Compressed Block Ack.
- Operational HT widths above 20 MHz, and a primary/secondary channel relationship for wider VHT modes.
diff --git a/plan/done/ieee80211-compressed-blockack-s3b-execution.md b/plan/done/ieee80211-compressed-blockack-s3b-execution.md
new file mode 100644
index 00000000000..b8076b55859
--- /dev/null
+++ b/plan/done/ieee80211-compressed-blockack-s3b-execution.md
@@ -0,0 +1,279 @@
+# S3b execution record
+
+## Completed S3b
+
+S3b is complete on branch `work/compressed-blockack-s3b` in [PR #1343](https://github.com/inet-framework/inet/pull/1343). The current source revision is `8646a9e6808ea8d21ac28bfc1271537469225fe9`, above PR base `bba1db4c8180a592894e0ab2a2dc056d9cb08082`. The broad verification below covers `8ba7c9e47bc3ded924cfa3917c8b899baccf789d`; a separate record covers the later allocation-index repair. Branch completion does not assert a merge into `master`. S4, A5, and production Compressed exchanges remain outside this completed stage.
+
+The six implementation commits supply optional selected extraction, lifecycle integration, first-DATA collision retirement, frame-sequence interruption ownership, fragment retirement, and allocation-index comparisons. A separate documentation commit records stage completion. The [S3b summary](ieee80211-compressed-blockack-s3b-summary.md) explains the resulting behavior. The [migration guide](../../doc/src/migration-guide/index.rst) defines custom-module changes. Basic remains the default; HCF rejects Compressed selection during initialization.
+
+The latest repair retains the highest transmitted allocation index in the agreement. The QoS data service assigns this local index before fragmentation; all fragments retain it in `Ieee80211DataSequenceTag`. Ordinary teardown discards unsent fragments through that index, even after ACK processing removes an earlier fragment. Older retransmissions cannot move the index backward, and wire sequence numbers remain unchanged.
+
+The fragment fixture transmits DATA with sequence 0. It allocates intervening DATA through the production data service and deletes that DATA without transmission. Fragment 0 of sequence 2048 then transmits. The index advances correctly; teardown removes unsent siblings of sequence 2048 and preserves later intact DATA. This case tests an allocation gap; it does not test lifetime expiry of the intervening DATA.
+
+| Verification at source revision `8ba7c9e47bc3ded924cfa3917c8b899baccf789d` | Result | Saved local artifact |
+| --- | --- | --- |
+| Debug compilation | Pass | `/tmp/pr1343-publish-debug.log` |
+| Release compilation | Pass | `/tmp/pr1343-publish-release.log` |
+| Selected debug module fixtures | 26 pass | `/tmp/pr1343-publish-modules.log` |
+| Functional fingerprint cases, without graphical `tyf` ingredients | 19 pass | `/tmp/pr1343-publish-fingerprints.log` |
+| Fragment reproduction against `53edefe95a6cc9671ef8d4864c913c2a5669306d` | Fails at the retirement assertion | `/tmp/pr1343-fragment-before.err` |
+| Fragment fixture after the repair | Four scenarios pass | `/tmp/pr1343-fragment-after.log` |
+| Commit, classification, include, parameter, and PR-description checks | Pass | `/tmp/pr1343-publish-mechanical.log` |
+| Source-seal check | Pass | `/tmp/pr1343-publish-seals.log` |
+
+The commands for this earlier verification run from the repository root after environment setup:
+
+```sh
+make MODE=debug -j12
+make MODE=release -j12
+inet_run_module_tests -m debug -f '(PacketSelectiveExtraction_1|Ieee80211BlockAck.*_1|Ieee80211EdcaDataLifetime.*|Ieee80211HcfInternalCollision_1)\.test$'
+```
+
+The functional fingerprint command runs from `tests/fingerprint`:
+
+```sh
+./fingerprinttest -d -F tyf -t 4 -m 'showcases/wireless/(blockack|aggregation|fragmentation|qos|txop)/|examples/wireless/qos/.*MacQos|examples/adhoc/qos/.*MacQos' examples.csv showcases.csv
+```
+
+Seven approved functional fingerprint rows belong to the lifecycle implementation. Later repairs retain those values and add no baseline change. Twelve controls retain their functional expectations. Graphical expectations remain unchanged and outside this functional result. The earlier verification at `667ae51dc9fbfbfac9ea68122bf65bc4f2d3e1df` includes 37 module fixtures, six unit fixtures, and 13 Basic demonstrations. Its protocol selection records four passes and four declared expected failures. Those earlier results do not establish final-head coverage for the same selections.
+
+At `8ba7c9e47b`, whole-project architecture and naming checks retain the findings present at the PR base. The interface check retains 15 findings and nine review notes; the two new interfaces pass. The changed source paths are unsealed. Raw `/tmp` artifacts are local evidence; they are not part of the published repository.
+
+### Allocation-index repair at `ea20a2880e`
+
+The series now places the allocation-index repair before the final plan commit. The repair maps from `ea20a2880e0ca607e680def1a875c6702c4ef249` to `8646a9e6808ea8d21ac28bfc1271537469225fe9` without source or test changes. The results below retain their original revision. The history comparison establishes content equivalence; it is not a new behavioral test.
+
+Commit `ea20a2880e0ca607e680def1a875c6702c4ef249` replaces cyclic comparisons with allocation indices. It adds a MSG definition for local metadata, without a wire-format change. Custom QoS data services must supply the tag, as the migration guide requires.
+
+| Recorded verification for the allocation-index repair | Result | Saved local artifact |
+| --- | --- | --- |
+| Debug compilation | Pass | `/tmp/s3b-push-debug.log` |
+| Release compilation | Pass | `/tmp/s3b-push-release.log` |
+| Fragment-retirement, lifecycle, association-reset, and DATA-selection fixtures in debug mode | Four fixtures pass | `/tmp/s3b-push-tests.log` |
+
+The commit records 50 successful simulation runs across these four fixtures. The fragment fixture supplies 20 runs with these parameters:
+
+- The initial sequence is 0 or 4095.
+- The allocation distance is 0, 2047, 2048, 2049, or 4096.
+- Teardown occurs during transmission or after the first fragment receives its Normal Ack.
+
+The fixture supplies accepted agreements and ACKs; production code performs fragmentation, expiry, retirement, and transmission. Later intact DATA and unrelated-peer DATA remain controls.
+
+These archived results do not extend the earlier 26-fixture or 19-fingerprint results to this revision. Release-mode behavioral tests, sanitizers, and other platforms remain unverified at this revision. The later audit checks the repair and documentation claims through source inspection; it is not a full independent correctness audit.
+
+This document correction changes no production source or recorded expectation. It repeats no build or simulation test. The checkout's existing `AGENTS.md`, four pending plans, and planning history remain under their existing owners.
+
+## Earlier execution snapshot
+
+The sections below retain the earlier implementation record, before publication and later repairs. Their candidate numbers and source pins refer to that earlier work. Their statements about absent commits, release checks, or baseline approval apply only to that snapshot. The completed-stage results above supersede those status statements.
+
+The original implementation parent is `41eee1d61978d62cfaf9d0d9c242b9fee590c20e`. The branch is `work/compressed-blockack-s3b`. The working copies of replacement plan revision 39 and lifecycle plan revision 29 define that scope. The user authorized S3b execution on 15 October 2026.
+
+## Implementation contract
+
+[HCF](../../src/inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.cc) owns agreement coordination and frame exchange order. The [originator](../../src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreementHandler.cc) and [recipient](../../src/inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckAgreementHandler.cc) handlers own their agreements and transient procedures. The MIB owns capability state. Management supplies an explicit peer reset before association replacement. The [recipient data service](../../src/inet/linklayer/ieee80211/mac/recipient/RecipientQosMacDataService.cc) owns duplicate checks and delivery. Queue components own extraction effects.
+
+The implementation retains Basic compatibility without an HT admission requirement. HCF continues to reject Compressed selection during initialization. Isolated policies verify Compressed admission with explicit capability inputs. S3b does not implement S4 BAR arbitration, A5 transfer completion, or Compressed exchanges.
+
+1. A1 returns complete accepted Block Ack Actions from the recipient data service. HCF dispatches each Action once after duplicate checks. A correlated Action resolves its procedure before obsolete exchange failure.
+2. A2 passes the selected profile to admission policies. The handlers correlate peer, TID, and dialog token. Accepted response values determine both agreement owners' parameters. The data service enforces both A-MSDU flags.
+3. A3a detaches the selected agreement before cleanup and deletion notification. Ordinary recipient teardown delivers complete buffered units in cyclic order. It discards incomplete units. HCF uses one absolute inactivity deadline across both roles.
+4. A3b clears both roles and obsolete peer work before deletion notifications. Management supplies the old peer identity before association replacement. The MIB keeps its boolean notification and coherent committed state.
+5. A3c routes Action disposal to its owner. HCF invalidates obsolete queued and in-progress Actions. An accepted transmission retains its context until safe completion. Valid DATA or BAR without an agreement causes recipient-role DELBA.
+6. A4 defers extraction during setup and recovery. The data service supplies a non-consuming sequence query. Optional selected extraction preserves queue ownership, residence metadata, and pull events. Contention uses the same eligibility condition.
+
+HCF obtains both roles' earliest active absolute deadlines after each agreement mutation. It schedules their minimum. It cancels the timer when neither role has a finite deadline. Expiration detaches every overdue agreement before the next deadline calculation.
+
+The delayed-successor interpretation preserves the existing setup trigger across separate DATA arrivals. A completed eligible Normal Ack transmission supplies the first condition. A later unexpired unnumbered successor supplies the second condition. The handler retains the first condition in its procedure map without an Action or timer. HCF promotes that state to setup intent before successor extraction. A single frame without a successor therefore causes no ADDBA.
+
+The implementation checks peer, TID, direction, modulo-4096 sequence order, and absolute versus relative time at each changed boundary. Duplicate Actions retain immediate ACK behavior. Invalid responses cannot resolve setup. Queue disposal, retry failure, response timeout, inactivity, received DELBA, and association reset have separate terminal paths. No MAC stop/start recovery claim applies.
+
+## Source assessment
+
+The parent recipient data service deletes Actions before it returns its accepted frames. HCF nevertheless dispatches the original management header after that call. Thus, duplicate detection does not control agreement dispatch. During a response wait, HCF passes an addressed Action to the active receive step instead of its agreement handler.
+
+The parent policies accept unchecked setup parameters. The recipient stores request size and timeout before it builds a response. The response omits the request dialog token. The originator policy asserts that an agreement exists but performs no response correlation.
+
+Both handlers supply absolute inactivity deadlines. HCF passes those deadlines to `rescheduleAfter`. The handlers select expiration with equality instead of an overdue comparison. Recipient DATA reception does not refresh the stored deadline. Received recipient DELBA uses the local receiver address for removal.
+
+The parent collision controller tests frame counts. This test does not establish agreement eligibility. A4 requires a production reproduction or a cancellation trace before a shared arbitration change.
+
+## Verification status
+
+The separate lifecycle review accepted the A1–A4 scope against the pinned parent. It required the shared inactivity minimum and explicit peer reset before equal-state reassociation. The review supplied source evidence; it supplied no implementation or runtime verdict.
+
+The parent debug build passed with `make MODE=debug -j12`. The command ran from the repository root and returned zero. Its log is `/tmp/s3b-parent-build.log`.
+
+The parent reproduction command was `inet_run_module_tests -m debug -f 'Ieee80211BlockAck(ActionDelivery|ActionDispatch|Negotiation)_1.test'`. Its log is `/tmp/s3b-parent-reproductions.log`. The Action delivery fixture failed its returned-frame count assertion. The negotiation fixture failed its dialog-token assertion. Both failures reached the production component after test initialization.
+
+The HCF dispatch fixture initially lacked a received-mode indication and a valid request sequence number. Those fixture setup errors supplied no product evidence. After correction, `inet_run_module_tests -m debug -f 'Ieee80211BlockAckActionDispatch_1.test'` reproduced duplicate dispatch. The fixture required one dispatch after an original request and its MAC retry. The parent dispatched both. Its log is `/tmp/s3b-parent-dispatch.log`.
+
+The generated dispatch executable also ran independently with `-u Cmdenv -r 1` from `tests/module/work/Ieee80211BlockAckActionDispatch_1`. At 1 ms, the fixture supplied a 5 ms absolute deadline. The parent scheduled 6 ms. The arrival-time assertion failed with exit status one. Its log is `/tmp/s3b-parent-inactivity.log`.
+
+New fixtures cover directional cleanup, association reset, response-wait order, deferred extraction accounting, and contention. Existing Basic recipient, selector, header, queue-disposal, lifetime, capability, and exchange fixtures remain controls. The candidate results below identify their commands and evidence limits.
+
+The parent control command selected nine module fixtures and passed all nine. Its command was `inet_run_module_tests -m debug -f 'Ieee80211(BlockAck(Reordering|Variant)|HcfHeader|RecipientNav|HcfMultipleProtection_2|SingleProtectionGroup|MgmtApHcfQueueDrop|MgmtRatePublication|MgmtStateCommit|EdcaDataLifetimeArbitration)_.*test'`. Its log is `/tmp/s3b-parent-controls.log`. This expression did not select the multiple-protection fixture because that alternative already contained its suffix.
+
+A second command selected that fixture and the Basic exchange control: `inet_run_module_tests -m debug -f 'Ieee80211(HcfMultipleProtection_2|BasicCompatibility_1)\.test'`. Both fixtures passed. Its log is `/tmp/s3b-parent-basic-controls.log`. Together, these results cover eleven fixtures, including all eight S1 recipient scenarios.
+
+The parent library hash was `ed6af79596a80e6192f4ca1cb8bf91f8a5f682d508eef58e95603796aa384a6e`. The lifecycle and response-wait fixtures also reproduced nine parent failures with that library. Directional teardown emitted a null deletion payload. Absent-agreement DATA reached upper delivery. Absent-agreement BAR caused no DELBA. An incoming Action received no immediate ACK during an active request ACK wait. `/tmp/s3b-regression-parent/` retains the exact executables, inputs, hashes, and individual run logs. Later fixture changes require fresh compilation before candidate verification.
+
+The selected protocol command was `inet_run_protocol_tests -p inet -m debug -w '^tests/protocol/wifi$' -f '(N_BlockAck|N_CompressedBa|N_Amsdu|Ac_BlockAck|WifiQosDelayedBa|Ac_MultiTidBa|N_Ampdu|Ac_Ampdu)\.test$'`. It returned zero with four passes and four declared expected failures. Its log is `/tmp/s3b-parent-protocol.log`. This run used the unchanged parent library while source implementation began. The protocol support library includes no changed agreement or HCF declaration. A new unused timeout parameter declaration was present; this result does not establish candidate behavior.
+
+The delayed-policy fixture failed its immediate-ACK assertion after BAR. The Multi-TID fixture missed its response deadline. Both A-MPDU fixtures missed their subframe-header observation. `/tmp/s3b-parent-reproductions/protocol-failure-causes.json` preserves these first failure causes. These outcomes remain unresolved protocol obligations.
+
+## Additional production reproductions
+
+Candidate 6 exposed two A4 failures before the final contention change. A real VO transmission held an ADDBA Request in its queue. A later VO arrival evicted that Request. HCF cleared setup, but surviving BE DATA obtained no new contention request. Action-disposal case 6 reproduced this failure without another BE arrival. `/tmp/s3b-actiondisposal-build6-reproduction/` retains the evidence.
+
+A separate fixture expired a VI agreement at 33 microseconds. Its queued DATA became ineligible before coincident VI and BE grants at 34 microseconds. VO had its next grant at 70 microseconds. The controller still counted VI and charged BE a collision retry. With retry limit one, BE discarded its valid DATA. `/tmp/s3b-deferred-collision-before-isolated.log` records this isolated failure.
+
+The first contention trace also contained a legitimate VO grant at 34 microseconds. That trace supplies no evidence for the shared correction. The corrected fixture changes only VO AIFSN to six. Its eligible-VI control retains the real collision and retry disposal. Both corrected cases use seed zero and the same binary and library.
+
+HCF now supplies each EDCAF with a query for current frame eligibility. The collision controller uses this query before it selects contenders. The query performs no expiration, extraction, or protocol mutation. HCF retains the existing Basic readiness condition; S4 still owns BAR-only work. After an Action terminal outcome, HCF requests contention for every category with eligible work.
+
+Candidate 7 passes both corrected contention cases. The deferred-VI case transmits BE DATA without a collision or retry disposal. The eligible-VI control retains its collision and BE retry disposal. Late Request eviction and successful DELBA acknowledgment also release BE DATA without another BE arrival. `/tmp/s3b-deferral-selection-collision-final.log` and `/tmp/s3b-regression-build7-pass/` retain these results.
+
+Candidate 7 also reproduces an Action recovery-state defect. An owned ADDBA Request causes a real RTS transmission under a low RTS threshold. The missing CTS charges its short retry counter before any Action transmission. Peer reset then leaves that counter at one because Action-attempt state remains false. `/tmp/s3b-actiondisposal-build7-rts-reproduction/` retains the exact failure. Cleanup must distinguish an untouched Action from an Action whose RTS exchange owns retry state.
+
+The retained `MacQosWithBlockAck` demonstration exposes a separate setup trigger defect. A-MSDU aggregation drains all queued DATA for each AP transmission. The next same-TID arrival occurs after that transmission, so the immediate successor check never starts AP setup. Candidate 7 therefore supplies only the client-to-AP Basic link. The required AP-to-server link remains absent. The delayed-successor state addresses this condition without an empty setup request or changed traffic.
+
+## Controlled setup measurements
+
+The deferral fixture compares response timeouts of 100 ms and 250 ms under equal traffic, rates, seed, and response queue residence. It holds the real recipient response in its queue for approximately 150 ms. It does not inject a response directly. Both cases use three DATA packets and seed zero. DATA uses 6 Mbps; Actions use 24 Mbps.
+
+| Observed interval or count | 100 ms timeout | 250 ms timeout |
+| --- | --- | --- |
+| Request queue admission to PHY start | 34 microseconds | 34 microseconds |
+| Request ACK reception | 0.000684020016 s | 0.000684020016 s |
+| Request ACK to accepted response | No accepted response | 150.070003336 ms |
+| Response queue residence | 100.080006672 ms before invalidation | 150.078003336 ms before transmission |
+| DELBA queue admission to ACK completion | 80.006672 microseconds | No DELBA |
+| Surviving DATA PHY start | 0.100780026688 s | 0.150832023352 s |
+| Recipient MAC receives surviving DATA | 0.100980030024 s | 0.151032026688 s |
+| Transmitted management frames | One Request and one DELBA | One Request and one Response |
+| Management airtime | 72 microseconds | 72 microseconds |
+| ACK airtime for management exchanges | 56 microseconds | 56 microseconds |
+
+The 100 ms deadline starts recovery before the queued response becomes eligible. The recipient invalidates that response when DELBA arrives. DELBA acknowledgment releases surviving DATA under Normal Ack. The 250 ms deadline permits the delayed response and releases DATA under Block Ack. That final DATA has no BAR within the fixture, so MAC reception does not establish upper delivery.
+
+`/tmp/s3b-build11-timing-100ms.log` and `/tmp/s3b-build11-timing-250ms.log` contain the exact events and PHY durations. `/tmp/s3b-build11-setup-timing.json` preserves source lines and event numbers. The fixture does not independently timestamp the transition into setup deferral. These observations establish timeout order and separate exchange costs; they establish no delivery guarantee or preferred timeout.
+
+Application measurements use the unchanged Block Ack showcase, General configuration, run zero, seed zero, and a fixed 0.5-second interval. The source sends 5,000 application packets of 700 bytes at 0.1 ms intervals. The debug parent delivers 330 packets, or 231,000 useful bytes. Final debug candidate 11 delivers 324 packets, or 226,800 useful bytes. Application goodput is therefore 3.696 Mbps and 3.6288 Mbps, respectively.
+
+Both application runs use `inet --debug -u Cmdenv -c General -r 0 --seed-set=0 --sim-time-limit=0.5s --record-vector-results=false --record-scalar-results=true --cmdenv-express-mode=true`. Their result directories are `/tmp/s3b-metric-parent-debug` and `/tmp/s3b-metric-candidate-11`. `opp_scavetool export -F CSV-R` exports both scalar files into `/tmp/s3b-metric-results-final.csv`. Useful bytes come from the destination application's `packetReceived:sum(packetBytes)` scalar. Earlier runs without an explicit mode supply no matched-mode comparison.
+
+The comparison parent checkout is `9b4b22258fff87401943d5046e0d18c18b70a558`. Its source and feature declarations equal the implementation parent. Its ignored build profile differs in unrelated optional features. This one fixed-seed comparison establishes only the observed application counts. It establishes no general performance improvement, optional-retry benefit, or agreement-reuse benefit. S4 retains transfer-completion and idle-gap comparisons.
+
+## Final source and focused verification
+
+Final candidate 11 contains A1, A2, A3a, A3b, A3c, and A4. HCF retains Basic as the default and rejects Compressed during initialization. The source review found no remaining correctness defect or unresolved architecture exception. No commit, publication, recorded baseline change, or seal change forms part of this execution.
+
+The final debug build command is `make MODE=debug -j12`. It returns zero; `/tmp/s3b-candidate-build-11.log` records the build. The library SHA256 is `f1ec379bd582e9c84b87e548059068231f9381d0dbd67bc6c4908f9e174e4608`. `/tmp/s3b-final-source-manifest.json` identifies all 72 changed source, test, and migration files, including untracked headers. Its parent remains `41eee1d61978d62cfaf9d0d9c242b9fee590c20e`.
+
+The final implementation separates Action transmission from ownership of RTS retry state. An RTS failure records ownership of the short retry counter. It does not mark the Action as transmitted. Invalidation and every terminal Action outcome clear that owned counter. A separate control preserves another peer's counter when an untouched Action uses the same Sequence Control field. The existing shared recovery map still uses that field without the peer address; this change does not redesign that map.
+
+The delayed-successor cases now defer later individual DATA and later A-MSDU batches before extraction. Both negotiate starting sequence number one and transmit first DATA with number one. The single-frame controls send no ADDBA. Real AP reassociation removes the waiting state under both legacy and HT configurations. A lone frame in the new association cannot inherit the old trigger.
+
+| Final debug selection | Result | Evidence |
+| --- | --- | --- |
+| Related module fixtures and existing controls | 29 fixtures pass | `/tmp/s3b-final-modules-6.log` |
+| Lifecycle, response wait, association reset, Action disposal, retired DATA | Five fixtures; 50 scenarios pass | `/tmp/s3b-regression-build11-runner.log`, `/tmp/s3b-regression-build11-pass/` |
+| Deferred selection, setup, lifetime, collision controls | Three fixtures; 18 scenarios pass | `/tmp/s3b-build11-deferral-selection-collision.log`, `/tmp/s3b-build11-*` |
+| Related unit fixtures | Six fixtures pass | `/tmp/s3b-final-unit-4.log` |
+| Retained Basic demonstrations | 13 cases pass | `/tmp/s3b-final-demonstrations-11/evidence.json` |
+| Selected protocol requirements | Four pass; four declared expected failures | `/tmp/s3b-final-protocol-4.log` |
+
+The module selections total 37 distinct fixtures. The lifecycle and deferral snapshots retain generated code, configuration, executables, output, and hashes. Their simulation outputs contain no runtime warnings, errors, or undisposed objects. Runner notices about optional Python packages do not describe simulation failures. All cases use debug mode; deterministic new scenarios use seed zero.
+
+The [migration guide](../../doc/src/migration-guide/index.rst) explains the changed custom-module contracts. The [selected-extraction contract](../../src/inet/queueing/contract/ISelectivePacketCollection.h) defines queue ownership and pull effects. The [Action disposal fixture](../../tests/module/Ieee80211BlockAckActionDisposal_1.test) covers queue loss and RTS cleanup. The [deferral fixture](../../tests/module/Ieee80211BlockAckDeferral_1.test) covers timeout order and delayed successors. The [collision fixture](../../tests/module/Ieee80211BlockAckDeferredCollision_1.test) separates deferred contention from a genuine collision.
+
+The exact root module selector is:
+
+```sh
+inet_run_module_tests -m debug -f '(Ieee80211BlockAck(ActionDelivery|ActionDispatch|Admission|Negotiation)_1|PacketSelectiveExtraction_1|Ieee80211(BasicCompatibility_1|BlockAckVariant_1|BlockAckReordering_1|HcfHeader_1|HcfMultipleProtection_[12]|SingleProtectionGroup_1|RecipientNav_1|PreparedCancellation_1|Txnav_1|HcfSingleProtection_1|VhtHtSupport_1|MgmtApHcfQueueDrop_1|MgmtRatePublication_1|MgmtStateCommit_1|EdcaDataLifetime.*))\.test$'
+```
+
+The additional module and unit selectors are:
+
+```sh
+inet_run_module_tests -m debug -f 'Ieee80211BlockAck(Lifecycle|ResponseWait|AssociationReset|ActionDisposal|RetiredData)_1.test'
+inet_run_module_tests -m debug -f 'Ieee80211BlockAck(DataSelection|Deferral|DeferredCollision)_1\.test'
+inet_run_unit_tests -m debug -f 'Ieee80211(HtModeSet_1|VhtHtCapabilities_1|ResponseModeSelection_1|MibPeerTeardown_1|QosDuplicateRemoval_1|BlockAckActionWire_1)\.test$'
+```
+
+All category commands run from the repository root. The Basic observer command is `python3 tests/module/basic-compatibility-evidence.py --output /tmp/s3b-final-demonstrations-11`. It retains the original topologies, traffic, aggregation settings, fragmentation cases, parameter studies, and Basic assertions. The AP-to-server link now supplies real setup, DATA, BAR, and positive receipt bits. This result closes the two-hop regression without a weaker assertion.
+
+The final protocol command repeats the eight-case parent selector exactly. Its runner returns zero with four expected failures. Delayed-policy verification now fails at step one because admission refuses delayed policy. Its parent first failed at the later immediate-ACK requirement. Multi-TID still misses step two; both A-MPDU cases still miss the step-zero subframe-header requirement.
+
+`/tmp/s3b-final-protocol-failure-causes.json` preserves those first failure causes. Their expected-failure markers remain unchanged. These results establish no delayed, Multi-TID, A-MPDU, or Compressed support. The passing `N_CompressedBa` fixture still exercises the explicit Basic compatibility path in this stage.
+
+Scoped architecture checks pass for `src/inet/linklayer/ieee80211` and `src/inet/queueing`. Their logs are `/tmp/s3b-final-architecture-wifi.log` and `/tmp/s3b-final-architecture-queue.log`. `check-source-seals.sh --diff` passes; `/tmp/s3b-final-source-seals.log` records the result. Every changed source target is unsealed. `git diff --check` also passes for the owned changes.
+
+The independent gate comparison uses the actual implementation parent. Wi-Fi naming retains the same 32 findings; queue naming retains the same one finding. Wi-Fi interfaces retain the two existing findings under `AV-CONTRACT-02`; queue interfaces pass. New compliant interfaces increase those counts from 48 to 49 and from 24 to 25. `/tmp/s3b-review-{parent,candidate}-{naming,interfaces}-{0,1}.log` retains both sides; zero denotes Wi-Fi and one denotes queueing. No new mechanical finding or exception-ledger change is required.
+
+The C++ checker could not execute its analysis for either subtree. Both `check-cpp.sh` calls return two because this checkout has no `compile_commands.json`. `/tmp/s3b-final-cpp-wifi.log` and `/tmp/s3b-final-cpp-queue.log` retain that prerequisite error. These calls supply no clang-tidy verdict. This execution supplies debug compilation and source review; release compilation and the full pre-push gates remain outside this unpublished work.
+
+## Queue traversal cost
+
+Candidate 9 exceeded the CPU limit in both ten-second ad hoc fingerprint cases. A bounded Block Ack-disabled comparison reached the same event 104692 at 0.8 seconds. The parent used 3.56 CPU seconds; candidate 9 used 7.56 CPU seconds. Source inspection identified a new membership check that continued after it found each aggregation candidate.
+
+An external counter measured indexed queue reads without a repository edit. The captured production stack reached that membership check through the normal QoS extraction path. Each indexed read starts at the linked-list head. The full scan therefore repeated increasingly long walks for every candidate. This cost also affected traffic with Block Ack disabled.
+
+| Same-event profile | Parent | Candidate 9 | Candidate 10 |
+| --- | --- | --- | --- |
+| Indexed queue reads | 861 | 861,869 | 2,605 |
+| Sum of requested indices | 1,285 | 775,865,982 | 2,142 |
+| Largest requested index | 3 | 3,114 | 3 |
+
+Candidate 10 stops each membership search at its first match. The service still checks every candidate's presence, uniqueness, eligibility, and A-MSDU permission before any source removal. The repair adds no state or interface. The final profile retains event 104692 and removes high-index traversal from this run. Its plain run uses 4.24 CPU seconds; concurrent host load limits interpretation of that timing.
+
+`/tmp/s3b-baoff-profile-parent.log`, `/tmp/s3b-baoff-profile-candidate.log`, and `/tmp/s3b-baoff-profile-candidate10.log` retain the profiles. `/tmp/s3b-queue-profile.cc` supplies the external counter. The source review accepts the bounded early-break correction. Candidate 10 passed its focused checks after that correction. Candidate 11 retains the same early break.
+
+The Block Ack-enabled profile then found another repeated indexed scan in HCF's DATA existence query. At 0.8 seconds and event 102123, candidate 10 made 7,894,493 indexed reads. Candidate 11 makes 12,236 reads at the same event. Time inside high-index traversal falls from 14.37 seconds to 0.061 seconds. `/tmp/s3b-baon-profile-candidate10.log` and `/tmp/s3b-baon-profile-candidate11.log` retain these profiles.
+
+HCF now uses the optional predicate query to find unexpired DATA for a peer and TID. PacketQueue traverses its linked queue with an iterator. A composite reports `UnsupportedPacketSelection` when its provider cannot supply that query. HCF catches only that outcome and retains its collection fallback. Unrelated queue or predicate errors still propagate. This adds no capability cache or protocol state.
+
+The data service first checks the ordinary advertised head at both selection points. An eligible head therefore needs no selected-extraction capability. If all frames defer, the service waits without mutation. If a deferred head hides eligible work, the service requires selected extraction before it changes ownership or sequence state. The three new composite cases verify these distinctions through a provider without the optional interface.
+
+## Accepted exchanges across reset
+
+The final reset checks also repair physical statistics and RTS-protected identity. Candidate 10 omits EDCAF sent or received observations after logical retirement, although accepted DATA and its ACK complete physically. The final HCF emits each physical observation once before the retired-state guard. That guard still suppresses obsolete acknowledgment, recovery, agreement, and rate effects. The three DATA completion cases verify one sent observation and one received ACK observation.
+
+A separate reproduction resets the peer after RTS acceptance and before DATA submission. The old DATA and its lifetime leave active ownership. After CTS, candidate 10 nevertheless accepts that retired DATA because it checks no active-frame membership. Its management sequences instead select again, which can produce null or another peer's frame. `/tmp/s3b-retired-rts-build10-reproduction/` retains six clean failure cases.
+
+Candidate 11 retains the original protected management pointer after CTS, as the DATA sequence already does. HCF refuses DATA or management submission when that packet no longer belongs to active in-progress work. The accepted RTS exchange can finish safely, but it cannot authorize a retired packet or substitute another peer's packet. All six reset-during-RTS cases pass with no post-reset protected submission. They also preserve the other peer's DATA lifetime.
+
+The independent review closes every reported source finding in candidate 11. Its general checklist reports 20 passes and 14 inapplicable items. Its WLAN checklist reports 12 passes and one inapplicable item. Neither checklist retains a flag or question. The review adds no exception-ledger or seal change.
+
+## Final fingerprint comparison
+
+All 19 final debug cases complete without a CPU-limit error. The repository expectations yield one pass and 18 mismatches. The actual parent yields four passes and 15 graphical-only mismatches. Twelve final cases match every calculated parent ingredient. Seven Basic cases change functional ingredients; the table below identifies them.
+
+| Case | Calculated `tplx` | Calculated `~tNl` | Calculated `~tND` | Calculated `tyf` |
+| --- | --- | --- | --- | --- |
+| Ad hoc `MacQos`, run 1 | `5fbb-9fd9` | `c0a4-97f6` | `2373-df01` | `49b6-dbab` |
+| Infrastructure `MacQosWithBlockAck`, run 0 | `f8a4-9474` | `1cba-cccc` | `538e-67df` | `69e0-5af3` |
+| Block Ack `Fragmentation`, run 0 | `021a-d29a` | `1597-ec6a` | `feff-7e88` | Not selected |
+| Block Ack `MixedTraffic`, run 0 | `5b9e-801f` | `8731-2504` | `9724-d1ce` | Not selected |
+| Block Ack `NoFragmentation`, run 0 | `b828-6bd6` | `026f-2c6d` | `2cd4-4795` | Not selected |
+| Fragmentation `HCFfragblockack`, run 0 | `f509-65f1` | `6b3a-0d65` | `c474-7174` | `4cfb-b3c7` |
+| TXOP `General`, run 0 | `f58d-d2fd` | `38f7-f642` | Not selected | `e520-9a34` |
+
+Paired production traces confirm the first changed exchanges. In `NoFragmentation`, both versions transmit DATA 0 at 0.210 ms and complete it at 0.496 ms. Both also number DATA 1 through the existing successor forecast. The parent immediately requests starting number one. The candidate first resolves numbered DATA 1 under Normal Ack, then requests starting number two. This is A4's required drain-before-setup rule.
+
+The TXOP trace likewise changes the request number from two to three after numbered DATA resolves. The fragmentation trace completes the numbered fragmented unit before ADDBA. The parent starts setup after fragment zero. The ad hoc trace changes the request number from two to three after numbered Voice fragments resolve. The infrastructure trace suppresses empty-successor setup after ARP and starts setup when later WWW DATA arrives. The retained demonstrations still show successful Basic exchanges, positive receipt bits, and fragment bits where required.
+
+`/tmp/s3b-fp-first-divergences.json` and `/tmp/s3b-fp-qos-first-divergences.json` retain times, events, commands, fields, and source lines. These paired windows establish the changed setup boundaries. They do not attribute every later counter difference to one mechanism. The final 19 ingredients equal candidate 10 exactly, so the final cost and reset repairs add no further fingerprint change.
+
+The parent command ran from its own `tests/fingerprint` directory after its `setenv` file. It used debug mode, four threads, and the same 19-case selector. `/tmp/s3b-parent-fingerprints-2.log` retains its complete result. `/tmp/s3b-parent-fingerprint-inventory.json` preserves its calculated ingredients before later jobs overwrote shared result paths. Interrupted invocations and overwritten shared outputs supply no comparison evidence.
+
+The final candidate uses private copies of the unchanged input CSV files in `/tmp/s3b-final-fingerprint-inputs-11`. `input-hashes.json` records their byte hashes. Older jobs cannot replace these comparison inputs or outputs. The exact command runs from `tests/fingerprint`:
+
+```sh
+PYTHONUNBUFFERED=1 ./fingerprinttest -d -t 4 -m 'showcases/wireless/(blockack|aggregation|fragmentation|qos|txop)/|examples/wireless/qos/.*MacQos|examples/adhoc/qos/.*MacQos' -l /tmp/s3b-final-fingerprint-inputs-11/runner.log /tmp/s3b-final-fingerprint-inputs-11/examples.csv /tmp/s3b-final-fingerprint-inputs-11/showcases.csv
+```
+
+`/tmp/s3b-candidate-fingerprints-6.log` records all 19 results and the nonzero mismatch status. `/tmp/s3b-fingerprint-comparison-build11.json` records the parent and candidate 10 comparisons. `/tmp/s3b-final-verification.md` supplies the regression analysis. The seven calculated rows above are evidence, not approved baseline values. Every recorded repository expectation remains unchanged.
+
+The separate capability observer also passes all eight inventory cases on candidate 11. Its command is `python3 tests/module/basic-compatibility-evidence.py --protocol-inventory --output /tmp/s3b-final-protocol-inventory-11`. The evidence file records actual HT state independently of the protocol assertions. Passing inventory observations do not convert the four expected protocol failures into supported features.
diff --git a/plan/done/ieee80211-compressed-blockack-s3b-summary.md b/plan/done/ieee80211-compressed-blockack-s3b-summary.md
new file mode 100644
index 00000000000..c09a1e343e9
--- /dev/null
+++ b/plan/done/ieee80211-compressed-blockack-s3b-summary.md
@@ -0,0 +1,251 @@
+# S3b: safe Block Ack agreement setup and teardown
+
+> **Kind:** reference · **Status:** snapshot 2026-10-15 · **Seal:** none · **Owns:** — · **Stands on:** [replacement summary](../pending/ieee80211-compressed-blockack-replacement-summary.md), [lifecycle plan](../pending/ieee80211-blockack-agreement-lifecycle.md), [execution record](ieee80211-compressed-blockack-s3b-execution.md)
+
+S3b repairs the agreement lifecycle that Compressed Block Ack will need. It controls when DATA can use an agreement, how agreement Actions change state, and how cleanup affects queued and received DATA. The immediate production target is Basic Block Ack. Production HCF still rejects Compressed selection at initialization.
+
+S3b is complete on branch `work/compressed-blockack-s3b` in [PR #1343](https://github.com/inet-framework/inet/pull/1343). The current source revision is `8646a9e6808ea8d21ac28bfc1271537469225fe9`, above base `bba1db4c8180a592894e0ab2a2dc056d9cb08082`. The [completion record](ieee80211-compressed-blockack-s3b-execution.md#completed-s3b) separates broad verification at `8ba7c9e47b` from focused verification of the later allocation-index repair. This status records branch completion; it does not assert a merge into `master`. The diagrams explain implemented behavior; they do not represent measured frame traces.
+
+## Why this stage exists
+
+A Block Ack agreement contains the shared parameters that permit Block Ack operation between an originator and a recipient. The originator sends DATA; the recipient receives DATA. ADDBA establishes the agreement through a request and response. DELBA ends the agreement through an Action frame. A BAR is a control frame that requests a Block Ack response.
+
+Safe DATA transmission requires more than a correct Block Ack bitmap. Both stations must agree on the parameters and on whether an agreement exists. Retries, lost responses, queue disposal, inactivity, and association replacement can change that state. S3b gives each event an owner and a defined effect on DATA.
+
+The parent reproductions expose concrete causes. The recipient service deletes accepted Actions, while HCF dispatches the original header without the duplicate result. The recipient response omits the dialog token that identifies its request. HCF also treats an absolute inactivity deadline as a relative delay. The [execution record](ieee80211-compressed-blockack-s3b-execution.md#source-assessment) gives the source assessment and reproduction limits.
+
+```mermaid
+flowchart LR
+ P["S1, S2, R1, S3a
Recipient progress, DATA lifetime,
management rates, profile selection"]
+ L["S3b
Agreement lifecycle
A1 through A4"]
+ B["S4
BAR-only channel access
and transfer completion"]
+ C["S5: C1 through C4
Compressed frame format,
exchanges, timing checks, default"]
+ P --> L --> B --> C
+```
+
+S3b supplies the lifecycle foundation. S4 supplies the final BAR exchange and transfer-completion behavior. S5 supplies complete Compressed exchanges and their verification. The [replacement summary](../pending/ieee80211-compressed-blockack-replacement-summary.md) owns this stage order. The replacement and lifecycle plans remain pending because their S4 and S5 work remains incomplete.
+
+## Who owns the state
+
+Agreement identity includes the local role, peer, and traffic identifier (TID). One station can hold both roles for the same peer and TID. These records describe opposite DATA directions. Teardown of one role must preserve the other role.
+
+| Component | Responsibility in S3b |
+| --- | --- |
+| HCF, the hybrid coordination function | Coordinates agreement Actions, frame exchanges, timers, DATA eligibility, and cleanup order. |
+| Originator agreement handler | Owns originator agreements, setup procedures, response deadlines, and recovery state. |
+| Recipient agreement handler | Owns recipient agreements and recipient teardown procedures. |
+| Agreement policies | Check setup parameters and profile admission. |
+| Originator QoS data service | Selects eligible DATA, checks A-MSDU permission, and assigns sequence numbers after extraction. |
+| Recipient QoS data service | Checks duplicates, returns accepted Actions, and prepares received DATA for delivery or disposal. |
+| Reorder owner | Holds received DATA behind sequence gaps and supplies ordered complete units at teardown. |
+| Queue components | Own extraction, ownership transfer, queue residence metadata, and pull events. |
+| Management and the MIB | Management identifies association reset. The management information base (MIB) owns capability and rate state. |
+
+The [execution contract](ieee80211-compressed-blockack-s3b-execution.md#implementation-contract) defines these responsibilities. HCF coordinates the existing owners; queues do not keep a second copy of agreement state.
+
+## A1: apply each accepted Action once
+
+HCF must distinguish reception of an Action from acceptance of that Action for agreement processing. A valid MAC retry still needs its immediate ACK. It must not create another agreement or apply DELBA twice. The recipient data service therefore returns only complete, nonduplicate Actions for HCF dispatch.
+
+```mermaid
+flowchart TD
+ RX["Addressed ADDBA or DELBA arrives"] --> ACK["Retain immediate ACK behavior
for valid received frames"]
+ ACK --> CHECK["Recipient data service checks
complete-frame state and duplicates"]
+ CHECK -->|Duplicate| STOP["No agreement dispatch"]
+ CHECK -->|Accepted complete Action| RETURN["Return Action to HCF"]
+ RETURN --> OWNER["Agreement owner checks identity
and applies the Action once"]
+```
+
+This diagram separates decisions; it does not prescribe ACK timing relative to dispatch. An Action during an active response wait needs an additional order rule. HCF first lets the agreement owner determine whether that Action resolves the pending procedure. HCF then ends the obsolete exchange without an obsolete terminal failure. The retained valid Action still receives its ACK.
+
+Hypothetical example: the peer receives ADDBA Request, but the request ACK is lost. A matched successful ADDBA Response arrives during the request's final ACK wait. The originator accepts the response before request-failure notification. Cleanup must not turn that success into teardown, DATA disposal, or another request retry. An unrelated Action does not suppress the interrupted exchange's ordinary failure result.
+
+The [Action contract](../pending/ieee80211-blockack-agreement-lifecycle.md#3-agreement-behavior) owns this order. The [HCF source](../../src/inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.cc) contains dispatch and response-wait integration. The frame-sequence handler owns the interruption outcome after recipient processing. It suppresses obsolete failure feedback for retired frames. An Action attempt that Tx already accepts retains its context until safe completion; logical retirement does not permit immediate destruction of that context.
+
+## A2: use the parameters that both stations accept
+
+An ACK for ADDBA Request confirms request reception. It does not establish that the originator accepted an agreement. The originator activates its agreement only after a matched successful ADDBA Response passes validation. The response must match the pending peer, TID, and dialog token. It must also specify immediate policy and supported parameters.
+
+```mermaid
+sequenceDiagram
+ participant O as Originator
+ participant R as Recipient
+ O->>R: ADDBA Request: TID, token, first sequence number
+ R-->>O: Immediate ACK for request
+ Note over R: Validate request and build accepted response
+ Note over R: Create recipient agreement from response values
+ R->>O: ADDBA Response: same token, accepted parameters
+ Note over O: Match peer, TID, token, and parameters
+ Note over O: Activate originator agreement
+ O-->>R: Immediate ACK for response
+ O->>R: DATA under the accepted agreement
+```
+
+The built-in recipient policy chooses `min(64, maximumAllowedBufferSize)` as the accepted buffer size. The request size gives guidance; it does not cap that choice. Both agreement owners use the response timeout value for inactivity. A zero response timeout disables inactivity expiration. The recipient keeps the request's first sequence number for its reorder state.
+
+Hypothetical example: the request suggests eight positions, but the recipient configuration permits 64. The response advertises 64, so both agreement owners use 64. The recipient must not retain eight while the originator uses 64. A rejected modification request leaves the existing agreement unchanged.
+
+A-MSDU permission also belongs to negotiation. An A-MSDU combines multiple MSDUs within one MPDU. Both the request and response flags must permit it before the originator constructs an A-MSDU under the agreement. If either flag denies permission, individual DATA can still use Block Ack. Normal Ack must not bypass a denial for an A-MSDU that already exists.
+
+Basic admission preserves non-HT and legacy ad hoc compatibility. Compressed admission requires local HT operation and valid peer HT state for the relevant direction. S3b checks Compressed admission through isolated policies; this does not enable production Compressed exchanges. The [negotiation contract](../pending/ieee80211-blockack-agreement-lifecycle.md#3-agreement-behavior) defines these conditions and policy choices.
+
+## A3: end the correct agreement without stale work
+
+A3a covers directional teardown and inactivity. A3b covers peer reset. A3c covers Action disposal and recovery when no agreement exists. These are review units within one lifecycle contract. The first retirement path must also invalidate obsolete Actions that could later change the retired state.
+
+### Directional teardown preserves the opposite role
+
+For received DELBA, HCF uses the transmitter address as the peer. The Initiator field identifies which local role must end. If the peer sends DELBA as originator, the local recipient agreement ends. If the peer sends DELBA as recipient, the local originator agreement ends.
+
+Ordinary recipient teardown first removes the agreement from lookup. The recipient data service then preserves duplicate state and obtains complete buffered units in cyclic sequence order. HCF delivers those units before the deletion event and resource release. Incomplete Basic units undergo disposal. Association reset uses a different policy: it discards old buffered units.
+
+```mermaid
+flowchart TD
+ START["Recipient agreement ends"] --> DETACH["Remove agreement from lookup
and invalidate obsolete Actions"]
+ DETACH --> KIND{"Association reset?"}
+ KIND -->|No| ORDER["Collect complete buffered units
in cyclic sequence order"]
+ ORDER --> DELIVER["Deliver complete units once
and discard incomplete units"]
+ KIND -->|Yes| DISCARD["Discard old buffered units
without upper delivery"]
+ DELIVER --> EVENT["Emit deletion event
and release agreement resources"]
+ DISCARD --> EVENT
+```
+
+Hypothetical Basic example: sequence 100 is absent, while complete units 101 and 102 remain behind the gap. Ordinary recipient teardown delivers 101 followed by 102 before cleanup. Acknowledgment of those units alone does not establish upper delivery. This release across a gap is an INET policy choice. The [migration guide](../../doc/src/migration-guide/index.rst) explains the implemented teardown policy.
+
+At ordinary originator teardown, HCF discards transmitted, unacknowledged DATA for the selected agreement. HCF also discards unsent fragments through the highest transmitted allocation index. The QoS data service assigns this local index before fragmentation; all fragments retain the same index. The agreement retains the highest transmitted index after ACK processing removes an earlier fragment. Allocation indices preserve order across large sequence gaps and wraparound; older retransmissions cannot move the boundary backward. Later intact DATA and another peer's DATA remain eligible.
+
+The fragment regression supplies a concrete example. Fragment 0 of sequence 4095 enters transmission before inactivity expires. HCF discards its unsent siblings at teardown and preserves the intact successor with sequence 0. The fixture also checks expiration after fragment 0 receives its Normal Ack. A separate collision regression checks disposal of the first DATA before transmission; HCF retires that agreement before its successor can use it.
+
+```mermaid
+flowchart LR
+ F0["Sequence 4095, fragment 0
Transmission accepted"] --> E["Inactivity expires
Retire originator agreement"]
+ E --> D["Discard unsent fragments
of sequence 4095"]
+ E --> K["Keep intact successor
with sequence 0"]
+ E --> P["Keep another peer's DATA"]
+```
+
+### Two timers serve different purposes
+
+The ADDBA response timer bounds setup after the request receives its Normal Ack. The inactivity timer bounds an active agreement without relevant activity. S3b uses separate timers because setup uncertainty and agreement inactivity require different state transitions.
+
+| Timer | Start or refresh condition | Expiration effect |
+| --- | --- | --- |
+| ADDBA response timer | Request ACK starts that peer/TID deadline. The default INET interval is 100 ms. | HCF starts teardown because the peer can hold an agreement. |
+| Agreement inactivity timer | Agreement activation starts its deadline. Relevant traffic refreshes it. | HCF retires each overdue agreement and starts DELBA. |
+
+HCF schedules the earliest absolute deadline across both agreement roles. It cancels the inactivity timer when no finite deadline exists. Expiration selects deadlines at or before the current time, so a late callback does not miss an agreement.
+
+```mermaid
+flowchart LR
+ O["Originator role
earliest active deadline"] --> MIN["HCF selects the minimum
absolute deadline"]
+ R["Recipient role
earliest active deadline"] --> MIN
+ MIN -->|Finite deadline| AT["Schedule at that time
or now if overdue"]
+ MIN -->|No finite deadline| CANCEL["Cancel inactivity timer"]
+```
+
+Hypothetical timing example: the current time is 1 ms and the absolute deadline is 5 ms. HCF schedules expiration at 5 ms. A relative delay of 5 ms would incorrectly schedule 6 ms. The execution record reports this exact parent failure. The [timeout contract](../pending/ieee80211-blockack-agreement-lifecycle.md#response-timeout-and-data-lifetime) distinguishes the INET response timeout from DATA lifetime.
+
+### Peer reset clears both roles before callbacks
+
+Association replacement ends the old association even when the replacement publishes identical rates and HT capabilities. A rate-change notification alone cannot identify that event. Management therefore passes the peer identity to HCF before replacement. HCF clears both roles and old peer work before the first agreement deletion callback.
+
+```mermaid
+sequenceDiagram
+ participant M as Management
+ participant H as HCF
+ participant A as Agreement owners
+ participant D as DATA and Action owners
+ participant V as Deletion observers
+ M->>H: Reset old peer before association replacement
+ H->>A: Detach both roles and pending procedures
+ H->>D: Invalidate old Actions and clear old peer DATA
+ H->>D: Discard old receive buffers and duplicate state
+ H->>V: Emit agreement deletion events
+ Note over V: Neither old role remains visible
+ M->>M: Complete association state replacement
+```
+
+With Block Ack enabled, reset discards the old peer's queued and in-progress QoS DATA, including unsent Normal Ack DATA without an agreement. Another peer retains its agreements, DATA, and lifetime records. The MIB continues to publish coherent committed state through its existing boolean notification. The [association-reset contract](../pending/ieee80211-blockack-agreement-lifecycle.md#reassociation-consumers-and-callback-order) explains why the separate reset handoff is necessary.
+
+An accepted physical exchange retains its context until completion. Physical statistics still record that exchange, but its callbacks cannot submit retired DATA after CTS. The frame-sequence handler destroys the context before it deletes deferred frame copies.
+
+### Queue loss and uncertain setup require explicit recovery
+
+HCF tracks each locally generated ADDBA or DELBA Action by frame identity, peer, TID, and role. Queue rejection, eviction, retry failure, and acknowledgment reach that Action's owner. Each Action produces one terminal transition. Intentional invalidation removes its procedure identity before queue disposal can report a second failure.
+
+If Tx never accepts a request attempt, local cancellation can release DATA under Normal Ack. After a possible request transmission, no response does not prove that the peer lacks an agreement. HCF must defer DATA until teardown confirms a common state. A successful recipient agreement remains active if its response suffers disposal; response loss does not undo recipient acceptance.
+
+```mermaid
+flowchart TD
+ P["ADDBA pending
DATA deferred"] -->|Matching success| A["Active agreement
release eligible DATA"]
+ P -->|Initial rejection or cancellation
before any accepted request attempt| N["No agreement
Normal Ack DATA can resume"]
+ P -->|Attempted request fails
or response deadline expires| T["DELBA pending
DATA remains deferred"]
+ T -->|DELBA receives ACK| N
+ T -->|Originator DELBA fails
or suffers queue disposal| R["Drop deferred batch
retain recovery-needed state"]
+ R -->|New DATA arrives| T
+```
+
+S3b treats a DELBA ACK as teardown confirmation for this INET profile. This policy assumes that the peer processes a valid DELBA. Failed originator DELBA drops the current deferred batch and leaves recovery-needed state without a periodic retry timer. New DATA triggers one fresh DELBA exchange; DATA that arrives during that exchange shares its result.
+
+Failed recipient-role DELBA clears its teardown procedure without an originator recovery record or disposal of opposite-direction DATA. Valid Block Ack-policy DATA or a valid BAR without an agreement triggers recipient-role DELBA. That DATA does not reach upper delivery, and that BAR does not create receive state. Malformed BAR input does not trigger this recovery path. The [lifecycle contract](../pending/ieee80211-blockack-agreement-lifecycle.md#3-agreement-behavior) owns these separate outcomes.
+
+## A4: preserve the first sequence number and unrelated DATA access
+
+The ADDBA request specifies the first sequence number for the agreement. Setup must not consume that number through premature extraction. After the first eligible DATA transmission, more unnumbered DATA for that peer and TID can trigger setup intent. Already numbered DATA first completes or drops under its existing policy. HCF then queries the next sequence number without consumption and creates ADDBA Request.
+
+Hypothetical example: the next unassigned number is 100. HCF requests an agreement with first number 100 while the affected DATA remains queued. After a matching response, extraction assigns 100 to the first DATA. If DATA 100 suffers terminal disposal before transmission, HCF starts teardown before DATA 101 can use that agreement. Extraction alone does not count as first transmission.
+
+Deferral applies to one peer and TID. It must not prevent another peer, another TID, or eligible management traffic from access. The data service selects the first eligible frame in the configured queue service order. HCF uses the same eligibility condition for DATA contention. A nonempty queue that contains only deferred DATA does not justify a new DATA contention request.
+
+```mermaid
+flowchart LR
+ subgraph Q["Configured queue service order"]
+ A["Peer A, TID 2
setup pending
deferred"]
+ B["Peer B, TID 5
eligible"]
+ C["Peer A, TID 2
deferred"]
+ A --- B --- C
+ end
+ B --> X["Extract B through its queue owner
and assign its sequence number"]
+ A -.-> W["Remain queued
with existing lifetime state"]
+ C -.-> W
+```
+
+Selected extraction must preserve ownership, queue residence metadata, pull events, and compound queue accounting. Arbitrary removal has different effects and cannot replace extraction. The optional `ISelectivePacketCollection` interface supplies an eligibility query and extraction of the selected frame. Built-in support covers `PacketQueue`, `PriorityScheduler`, and `CompoundPacketQueueBase`.
+
+An external queue needs this interface only when deferred head DATA hides eligible work. Its ordinary dequeue path remains usable when its next frame is eligible. If every frame is deferred, the service waits. If eligible work requires an unsupported bypass, the service reports a diagnostic before mutation. The [selected-extraction contract](../../src/inet/queueing/contract/ISelectivePacketCollection.h) defines these operations.
+
+Before A-MSDU construction, the service checks one proposed source list. The first source must match the selected frame; every source must remain eligible and have permission. An invalid list causes individual extraction of the selected frame. Other sources retain their queue, lifetime, and sequence state. This prevents aggregation that bypasses deferral or the configured service order.
+
+## Completed verification
+
+The [completion record](ieee80211-compressed-blockack-s3b-execution.md#completed-s3b) reports broad results for source revision `8ba7c9e47bc3ded924cfa3917c8b899baccf789d`. Debug and release compilation pass at that revision. All 26 selected debug module fixtures and 19 functional fingerprint cases pass at that revision. The functional selection excludes graphical `tyf` expectations.
+
+The later allocation-index repair records debug and release compilation and four debug fixtures with 50 successful simulation runs. Those fixtures cover fragment retirement, lifecycle behavior, association reset, and DATA selection. The fragment fixture supplies 20 runs across sequence gaps, wraparound, and teardown before or after ACK reception. These results belong to original revision `ea20a2880e`, which maps to `8646a9e680` without source or test changes. The history comparison establishes content equivalence; it is not new behavioral evidence. This document correction repeats no build or simulation test.
+
+| Verified behavior | Relevant fixtures in this checkout |
+| --- | --- |
+| Accepted Actions return once; duplicates retain ACK behavior | [Action delivery](../../tests/module/Ieee80211BlockAckActionDelivery_1.test), [Action dispatch](../../tests/module/Ieee80211BlockAckActionDispatch_1.test) |
+| A matching Action resolves setup before obsolete exchange failure | [Response wait](../../tests/module/Ieee80211BlockAckResponseWait_1.test) |
+| Accepted parameters and profile admission reach their owners | [Negotiation](../../tests/module/Ieee80211BlockAckNegotiation_1.test), [Admission](../../tests/module/Ieee80211BlockAckAdmission_1.test) |
+| Directional cleanup and absent-agreement recovery preserve other state | [Lifecycle](../../tests/module/Ieee80211BlockAckLifecycle_1.test) |
+| Association replacement clears both roles before observers run | [Association reset](../../tests/module/Ieee80211BlockAckAssociationReset_1.test) |
+| Setup deferral preserves sequence state and unrelated progress | [Deferral](../../tests/module/Ieee80211BlockAckDeferral_1.test), [DATA selection](../../tests/module/Ieee80211BlockAckDataSelection_1.test) |
+| Selected extraction preserves queue effects | [Selective extraction](../../tests/module/PacketSelectiveExtraction_1.test) |
+| First-DATA disposal retires the agreement before successor transmission | [Deferred collision](../../tests/module/Ieee80211BlockAckDeferredCollision_1.test) |
+| Teardown removes unsent fragments through the highest transmitted allocation index | [Fragment retirement](../../tests/module/Ieee80211BlockAckFragmentRetirement_1.test) |
+
+The earlier source revision `667ae51dc9fbfbfac9ea68122bf65bc4f2d3e1df` supplies broader module, unit, protocol, and Basic demonstration results. Those results remain separate from verification of the allocation-index repair. The later focused results also do not extend the earlier 26-fixture or 19-fingerprint results to the current source revision. Seven approved functional fingerprint rows belong to the lifecycle implementation; the later repairs change no baseline. Graphical expectations remain unchanged.
+
+The later audits inspect the repair and documentation claims; they do not establish a full independent correctness audit. The archived local results establish no release-mode behavioral tests, sanitizer results, or other-platform results for the allocation-index repair. CI results remain separate from these local records.
+
+## Scope limits
+
+S3b does not establish these later capabilities:
+
+- Production Compressed BAR and Block Ack exchanges, serialization, or receipt-state behavior.
+- S4 BAR arbitration, BAR-only channel access, or teardown after the final Block Ack exchange.
+- Compressed response-rate and airtime correctness.
+- General MAC stop/start recovery.
+- A performance guarantee across repeated setup and teardown.
+
+S3b supplies a defined agreement lifecycle with safe DATA eligibility and cleanup. Later stages can add Compressed exchanges without a separate lifecycle mechanism. Their own production evidence remains necessary.
diff --git a/plan/pending/ieee80211-ampdu-implicit-blockack-summary.md b/plan/pending/ieee80211-ampdu-implicit-blockack-summary.md
new file mode 100644
index 00000000000..39d36d79585
--- /dev/null
+++ b/plan/pending/ieee80211-ampdu-implicit-blockack-summary.md
@@ -0,0 +1,372 @@
+# A-MPDU with implicit Compressed Block Ack: plan summary
+
+> **Kind:** reference · **Status:** draft · **Seal:** none · **Owns:** — · **Stands on:** [feature plan](ieee80211-ampdu-implicit-blockack.md), [PHY plan](ieee80211-ampdu-phy-reception.md)
+
+This plan proposes several independently acknowledged MPDUs in one HT transmission. The recipient sends one Compressed Block Ack after the transmission ends. The aggregate requests that response without a separate BAR. The originator retries only the MPDUs that remain unacknowledged.
+
+This summary describes revision 12 of the [feature plan](ieee80211-ampdu-implicit-blockack.md) and revision 11 of the [PHY plan](ieee80211-ampdu-phy-reception.md), dated 2026-10-17. Their source reference is branch `work/compressed-blockack-s5` at `4fa5cc0de15ee57388bcb3f20e866cab5972a670`. The feature remains proposed. The diagrams describe the design, not executed simulation traces.
+
+The PHY plan supplies aggregate reception before the MAC feature uses it. Its first gate proves deterministic member outcomes and includes the shared NIST/Yans implementation. It also preserves default HT operation and reception without an error model. Its second gate extends the NIST/Yans and compatibility evidence. The MAC feature can reach deterministic integration between those gates.
+
+Execution status, 2026-10-17: PHY P0 through P4 and gates D1 and D2 are complete under the user's approved merge exception. The [execution record](ieee80211-ampdu-phy-execution.md) states the checks and limits. P3 verifies NIST/Yans range probabilities and seeded outcomes. P4 compares 576 ordinary reception records with the pinned parent. All records match exactly. MAC aggregate receipt and implicit Block Ack remain pending.
+
+Implementation waits for merged PRs [#1343](https://github.com/inet-framework/inet/pull/1343), [#1344](https://github.com/inet-framework/inet/pull/1344), and [#1345](https://github.com/inet-framework/inet/pull/1345). Read-only source checks can occur now. Both plans require a refreshed merged base before source implementation. This is the agreed delivery policy for one shared S5 baseline.
+
+## What changes after S5
+
+S5 is the stage that supplies explicit Compressed BAR exchanges for individual MPDUs. The originator sends those MPDUs in separate PPDUs. It later sends BAR to request their receipt status. The proposed path puts several MPDUs into one A-MPDU and requests receipt status through that aggregate. [Problem and intended behavior](ieee80211-ampdu-implicit-blockack.md#1-problem-and-intended-behavior).
+
+This illustration assumes an accepted agreement and three MPDUs. Both paths end with a Compressed Block Ack. The proposed path needs fewer separate PHY transmissions for those MPDUs. Protection, contention, response rates, and loss still affect goodput; the plan promises no fixed performance gain.
+
+```mermaid
+flowchart TB
+ subgraph Current["Current S5: explicit request"]
+ direction LR
+ C1["DATA 100
One PPDU"] --> C2["DATA 101
One PPDU"]
+ C2 --> C3["DATA 102
One PPDU"]
+ C3 --> CB["BAR
One PPDU"]
+ CB --> CR["Compressed Block Ack"]
+ end
+ subgraph Proposed["Proposed: implicit request"]
+ direction LR
+ A["One A-MPDU in one PPDU
MPDUs 100, 101, 102"] --> R["Compressed Block Ack
after SIFS"]
+ end
+```
+
+An A-MSDU and an A-MPDU combine different units. An A-MSDU combines MSDUs inside one MPDU. An A-MPDU combines complete MPDUs, each with its own FCS. Separate receipt bits therefore permit selective retry within the aggregate.
+
+## What the aggregate contains
+
+One common PHY preamble and header serve the complete aggregate. Each MPDU retains its MAC header, payload, sequence number, and FCS. A delimiter identifies each MPDU boundary and length. The aggregate has no additional outer MAC header or outer FCS. [Representation and limits](ieee80211-ampdu-implicit-blockack.md#42-eligibility-and-limits).
+
+PHY P1 extends the modeled HT-SIG fields and their serializer together. HT-SIG is the HT signal field that carries PSDU length and the Aggregation property. Golden bytes and independent decode must preserve both values in the 48-bit header. A singleton must retain Aggregation equal to one after byte conversion. This work covers the modeled HT-SIG header without a new waveform model. [HT-SIG representation and verification](ieee80211-ampdu-phy-reception.md#41-representation-and-bounded-ranges).
+
+The radio already assigns that 48-bit header length. P0 records its current value and the mode/PSDU duration path. P1 completes byte conversion within the same length; ordinary HT controls must preserve airtime under identical inputs. Changed byte content alone establishes no airtime change or reason to update a fingerprint expectation.
+
+Correct bytes alone do not establish rejection of an invalid received header. P1 also defines one stateless HT-SIG validator. P2 applies it before member outcomes and before radio decapsulation trusts HT Length. Invalid CRC or unsupported fields cause HEADER failure without delimiter or member draws. The current header-failure paths prevent receipt updates, delivery, and responses. [HT-SIG receive validation](ieee80211-ampdu-phy-reception.md#ht-sig-receive-validation).
+
+A separate bounds check refuses modeled PSDU content shorter than a valid header's HT Length. HEADER retains success and the decoded Aggregation property. DATA reports failure without range outcomes or draws, and MAC refuses the content before header or trailer peeks. Decapsulation skips length-based trailer removal. This case proves bounds safety for malformed input. It establishes no physical carrier-loss behavior or acceptance of a received prefix.
+
+The diagram shows two members. Nonfinal HT subframes need alignment padding of zero to three octets. The final HT subframe has no alignment padding. Extra zero-length delimiters can provide the minimum MPDU start spacing that the receiver requires.
+
+```mermaid
+flowchart TB
+ PHY["One common HT PHY preamble and header"] --> BODY
+ subgraph BODY["One PSDU: the A-MPDU"]
+ direction LR
+ D1["Delimiter
4 octets"] --> M1["MPDU 100
Header, MSDU, FCS"]
+ M1 --> P["Alignment padding
0 to 3 octets"]
+ P --> S["Zero-length delimiters
if spacing requires them"]
+ S --> D2["Delimiter
4 octets"]
+ D2 --> M2["Final MPDU 101
Header, MSDU, FCS"]
+ end
+```
+
+Each complete HT member must fit the 4095-octet delimiter limit. The whole aggregate must fit both the peer's advertised limit and the HT maximum of 65535 octets. Its airtime must also satisfy the PHY and TXOP limits. A byte count alone cannot establish that an aggregate qualifies.
+
+## Which traffic can use the first version
+
+The first transmit profile uses packet-level `Ieee80211Radio`, scalar or dimensional reception, HT mixed format, 20 MHz, and long GI. Aggregate transmission requires fixed DATA rates and no HCF rate-control submodule. Each aggregate serves one recipient and TID under an accepted immediate Compressed agreement. Compressed responses retain the 64-bit bitmap. Multiple peers and agreements can coexist; each retains separate state. [Supported profile](ieee80211-ampdu-implicit-blockack.md#first-supported-profile).
+
+Receive support also covers advertised HT20 short-GI modes at the permitted MCS and NSS values. Before the MIB update, the MAC supplies its exact mode set, effective channel widths, and NSS limit for receiver validation. The check covers advertised modes; unsupported catalog modes outside those limits do not cause rejection. A peer can select an advertised short-GI receive mode even when the local aggregate transmitter uses long GI. The new transmit policy remains limited to long GI. [Required receive coverage](ieee80211-ampdu-phy-reception.md#42-optional-role-and-shared-implementation).
+
+HCF coordinates eligibility and the exchange. Management and the MIB supply local and peer capabilities. The proposed HT policy selects members within those capabilities and the agreement's transmit window. The policy cannot grant an agreement or bypass a PHY limit.
+
+```mermaid
+flowchart TD
+ P{"Does the transmitter select
the new HT aggregation policy?"}
+ P -->|No| OLD["Use the current individual-frame path"]
+ P -->|Yes| H{"Do the actual HT mode
and peer state qualify?"}
+ H -->|No| FALL["Apply current fallback rules
No A-MPDU transmission"]
+ H -->|Yes| A{"Is an immediate Compressed
agreement active for this peer and TID?"}
+ A -->|No| SETUP["Use current setup or fallback
No aggregate before acceptance"]
+ A -->|Yes| L{"Do members, spacing, window,
length, and airtime qualify?"}
+ L -->|No| SMALL["Reduce the batch or use
an eligible individual frame"]
+ L -->|Yes| TX["Prepare one aggregate exchange"]
+```
+
+The transmit feature requires an explicit policy choice. The default policy continues to select no A-MPDU. A receiver must recognize supported received aggregates even when its own transmitter uses the default policy. Absent peer HT state retains S5's Normal Ack fallback.
+
+The WLAN receiver checks role presence at PHY initialization. The MAC requests exact coverage validation later, before its MIB update at link-layer initialization. Absent roles and incomplete advertised coverage cause failure before management sends capability information. These checks apply even when local transmit aggregation remains disabled. Non-HT custom models retain ordinary MPDU reception without that role. [Early compatibility checks](ieee80211-ampdu-phy-reception.md#44-model-detail-and-diagnostics).
+
+HT length exponent zero still advertises 8191 octets; it cannot disable aggregate reception. A model that exposes the role but lacks advertised short-GI support therefore fails initialization. A one-antenna configuration must pass when the model supports every advertised mode. The catalog can contain unsupported modes for more spatial streams. The validation adds no second capability store or independent filter.
+
+BerTable remains outside the HT aggregate profile. Its current initializer rejects HT operation strings before the new role check. This delivery adds no aggregate role to BerTable. The migration guide directs supported HT configurations to NIST or Yans. The PHY fixture compares a non-HT parent control and verifies the current HT refusal separately from the custom-model role diagnostic. [Compatibility decision and checks](ieee80211-ampdu-phy-reception.md#5-affected-components-and-compatibility).
+
+An absent error model retains the current error-free abstraction after PHY acquisition, possibility, attempt, and SNIR checks. Successful common checks permit physical success for bounded delimiter and member ranges without new error draws. Delimiter, FCS, address, and agreement checks still control MAC acceptance. This case adds no replacement module or interface requirement.
+
+The proposed settings are:
+
+```ini
+**.mac.hcf.isBlockAckSupported = true
+**.mac.hcf.blockAckVariant = "compressed"
+**.mac.hcf.rateControl.typename = ""
+**.mac.hcf.rateSelection.dataFrameBitrate = 6.5Mbps
+**.mac.hcf.rateSelection.dataFrameBandwidth = 20MHz
+**.mac.hcf.rateSelection.dataFrameNumSpatialStreams = 1
+**.mac.hcf.originatorMacDataService.mpduAggregationPolicy.typename = "HtMpduAggregationPolicy"
+```
+
+`HtMpduAggregationPolicy` does not exist yet. The fixed rate above selects HT MCS 0 with one spatial stream under the required HT20 long-GI configuration. These settings also need compatible modes, usable peer HT state, and accepted agreements. An `ac` interface qualifies only through an actual supported HT transmission. It does not establish VHT A-MPDU support. [Configuration and compatibility](ieee80211-ampdu-implicit-blockack.md#5-affected-components-and-compatibility).
+
+## How partial loss leads to selective retry
+
+Consider this hypothetical exchange with MPDUs 100 through 103. The recipient receives 100, 102, and 103 correctly. MPDU 101 fails. The recipient reports those separate results in one bitmap after the complete PPDU and SIFS.
+
+```mermaid
+sequenceDiagram
+ participant O as Originator HCF
+ participant R as Recipient PHY and HCF
+ participant U as Recipient upper protocol
+ Note over O,R: Accepted immediate Compressed agreement for one peer and TID
+ O->>R: One A-MPDU: 100, 101, 102, 103
+ Note over R: 101 fails. Retain receipt evidence for 100, 102, 103.
+ R->>O: After SIFS: bitmap 100=1, 101=0, 102=1, 103=1
+ Note over O: Release acknowledged MPDUs. Retain 101 with its original deadline.
+ O->>R: Singleton A-MPDU: retry 101
+ R->>O: After SIFS: Compressed Block Ack
+ Note over R,U: Apply reorder rules. Deliver each MSDU exactly once.
+```
+
+The originator releases 100, 102, and 103 from its transmit store. It retains 101 until acknowledgment or terminal disposal. The retry preserves 101's sequence number and absolute deadline. A singleton A-MPDU still requests Block Ack. One member does not turn it into a standalone MPDU with an individual ACK.
+
+The wire ACK policy bits remain `00`. In a standalone MPDU, those bits ordinarily mean Normal Ack. In the supported aggregate context, they mean Implicit BAR. The PHY aggregation property and MAC exchange context preserve that distinction. [Implicit exchange and recovery](ieee80211-ampdu-implicit-blockack.md#47-originator-exchange-timing-and-recovery).
+
+## Why preparation must preserve the source frames
+
+The MAC must know whether the complete exchange fits before it removes source MSDUs from their queues. The QoS data service therefore creates previews first. HCF selects one DATA mode and checks the batch with that mode. A refusal leaves the source MSDUs, deadlines, and sequence counter intact. [Preparation and ownership](ieee80211-ampdu-implicit-blockack.md#43-preparation-commitment-and-ownership).
+
+The current fixed-rate rules select the DATA mode by recipient before the service calculates mode-dependent spacing and aggregate length. If padding makes the batch too large, HCF reduces it and checks the smaller batch at that same mode. The design adds no size-aware DATA rate-control API.
+
+HCF rejects aggregate transmit opt-in with a rate-control submodule, also when fixed-rate settings would bypass that controller's rate choice. AARF can change its rate and probe state after a member callback. Separate callbacks for one PPDU can therefore describe a probe at a rate that no transmission used. The first profile avoids that false result through fixed rates. Adaptive aggregate feedback needs a separate design; existing individual-frame configurations retain their current rate-control behavior.
+
+The proposed policy selects transmitted retries first, numbered unsent MPDUs second, and queued new members last. Both committed groups use cyclic sequence order; queued new members use queue order. This order is an INET choice. Previews of queued members use candidate numbers without counter advancement. Commitment validates that reference before actual assignment. Already numbered members retain their numbers and deadlines.
+
+An internal collision can cause the current HCF handler to extract and number one frame without a DATA transmission. The EDCAF that loses the collision retains its current single-frame recovery, including terminal disposal. Aggregate preparation accepts a retained numbered frame without another sequence allocation. Current acknowledgment state keeps Retry clear for its first DATA transmission. The feature plan's [source-state table](ieee80211-ampdu-implicit-blockack.md#43-preparation-commitment-and-ownership) defines that distinction.
+
+A1 supplies capability propagation and aggregate construction calculations for spacing, serialization, and member layout. A3 uses those calculations for dynamic member selection, spacing at the retained mode, and batch reduction. A3 also retains canonical member ownership. [Implementation responsibilities](ieee80211-ampdu-implicit-blockack.md#6-implementation-steps-and-dependencies).
+
+```mermaid
+flowchart TD
+ Q["Source MSDUs remain in their queues"] --> P["Create previews
Select one DATA mode"]
+ P --> F{"Does a permitted batch fit?"}
+ F -->|No| KEEP["Preserve queue contents,
deadlines, and sequence state"]
+ F -->|Yes| PROTECT["Perform RTS/CTS if required"]
+ PROTECT --> START["At the existing Tx IFS boundary
Call HCF before actual submission"]
+ START --> V{"Are sources, agreement,
deadlines, and modes still valid?"}
+ PROTECT -->|RTS fails| RTSFAIL["Release unused previews
Apply one contention failure"]
+ V -->|No| CANCEL["Cancel the unsent aggregate
Create no DATA failure feedback"]
+ V -->|Yes| COMMIT["Commit new members
Assign their sequence numbers"]
+ COMMIT --> STORE["Transmit store retains
independent MPDUs"]
+ STORE --> FINAL["Finalize each member header
Calculate its own FCS"]
+ FINAL --> ENV["Aggregate envelope references
immutable member content"]
+ ENV --> TX["Transmit one PPDU"]
+ TX --> FREE["Release the envelope
Retain unresolved MPDUs"]
+```
+
+The transmit store, `InProgressFrames`, owns each committed MPDU until acknowledgment, expiration, or another terminal outcome. The envelope holds references to member content. Envelope disposal must not destroy the MPDUs that still need retry. Each MPDU retains its own lifetime; aggregate construction does not restart it.
+
+DATA submission means the actual call from Tx to the radio. Tx acceptance before SIFS leaves source MSDUs uncommitted. At its existing IFS timer boundary, Tx calls HCF before it copies or sends the PSDU. HCF validates the batch before the service commits new members. Tx then finalizes each member and submits the envelope within that synchronous call chain. Zero IFS uses the same callback immediately.
+
+HCF sets each member's Retry bit, ACK policy, and common Duration/ID. Tx applies the local TA and calculates each member's FCS after those header edits. The builder then constructs the delimiter envelope without an outer MAC header or outer FCS. Selective retry requires a fresh member FCS after any header change. [Member finalization](ieee80211-ampdu-implicit-blockack.md#43-preparation-commitment-and-ownership).
+
+If the originator inactivity timer retires the agreement during SIFS, the start callback refuses the cancelled preparation. No DATA reaches the radio, and no source receives a new sequence number. The frame sequence context retains the preparation until submission or cancellation. Reset cancels the pending Tx request before it releases that context.
+
+With an active agreement, aggregate RTS failure applies one contention update for new members, committed members, or both together. It adds no member retry increment, DATA attempt, DATA outcome, or pending probe. Queued sources remain queued; committed members retain their numbers, acknowledgment state, recovery state, and deadlines. The contention retry boundary resets access-category state without member disposal. Independent expiry or agreement retirement can still remove members. Current individual-frame RTS recovery and internal collision recovery retain their existing rules, including suppression of obsolete completion feedback.
+
+## Why the PHY needs separate member results
+
+The current receiver obtains a whole-data success decision. That decision cannot represent one failed MPDU beside three successful MPDUs. The plan adds separate delimiter and MPDU outcomes, with common PHY acquisition and header success. [Partial reception design](ieee80211-ampdu-implicit-blockack.md#44-phy-representation-and-partial-reception).
+
+```mermaid
+flowchart TD
+ H{"Did PHY acquisition
and header reception succeed?"}
+ H -->|No| NONE["No valid aggregate content"]
+ H -->|Yes| E["Score every delimiter and MPDU
Leave alignment padding unscored"]
+ E --> C["Reuse PREAMBLE and HEADER decisions
Retain DATA outcomes and derive WHOLE"]
+ C --> D["Decode bounded delimiter chains
Preserve any continuity break"]
+ D --> V["Set each failed member Packet flag
Apply FCS and address checks"]
+ V --> GOOD["Valid members update
receipt and reorder state"]
+ V --> BAD["Invalid members cause
no receipt or success update"]
+ GOOD --> END{"At PPDU end, does a valid
addressed member request a response?"}
+ BAD --> END
+ END -->|Yes| BA["Send one Compressed Block Ack
after SIFS"]
+ END -->|No| SILENT["Send no Block Ack"]
+```
+
+The cache keeps separate entries for each receiver, transmission, and signal part. PREAMBLE preserves the current per-part checks without early HEADER or DATA calculation. HEADER reuses PREAMBLE before its own header calculation. DATA owns bounded range outcomes or structural refusal after successful header reception. A WHOLE query derives its summary from those entries without another error draw. Aggregate success queries must use those same entries.
+
+In separate-part mode, PREAMBLE or HEADER failure aborts the attempted reception before a container reaches MAC. Existing radio state notifications let HCF invalidate its physical history. Whole-signal mode can supply a failure container that MAC rejects before header or trailer parsing. Both paths prevent acceptance, responses, and range draws. The design preserves their existing event boundaries.
+
+The PHY scores every four-octet delimiter, also when its MPDU length is zero. It scores each nonzero MPDU range once. Alignment padding still contributes airtime but has no separate error draw. The model cannot represent errors confined to alignment padding. Header failure prevents all range draws. A failed spacing delimiter preserves a continuity break even if a later delimiter becomes recoverable.
+
+P0 checks cache insertion, replacement, cleanup, and direct success queries against the merged base. P2 checks PREAMBLE at its actual boundary before any HEADER or DATA calculation. Later HEADER, DATA, WHOLE, and repeated result queries must reuse their retained outcomes. The [PHY design](ieee80211-ampdu-phy-reception.md#43-decision-ownership-and-random-draws) defines that ownership. The aggregate result path preserves valid members despite a failed PPDU summary.
+
+With a present error model, aggregate results retain `ErrorRateInd` with all three probability fields set to `NaN`. The new range model supplies retained outcomes without an aggregate probability estimate. Result construction must preserve applicable power, SNIR, timing, mode, and channel metadata. Without an error model, the common base's existing probability tag presence and exact values remain valid. Ordinary MPDU metadata keeps its current calculations. [Aggregate metadata contract](ieee80211-ampdu-phy-reception.md#aggregate-error-rate-metadata).
+
+The tag contract permits `NaN` for unknown probabilities. P0 must audit readers, filters, and recorders that can receive aggregate metadata before P2 implements this contract. A reachable incompatibility blocks that implementation until the PHY plan records its resolution. Tag omission requires evidence that all affected consumers accept absence. The PHY fixture must verify the identified consumer paths before D1 closes.
+
+The [PHY plan](ieee80211-ampdu-phy-reception.md#42-optional-role-and-shared-implementation) defines an optional WLAN role for per-range probabilities and a pure mode-support query. A shared WLAN base implementation reuses the current length-based calculations for NIST, Yans, and compatible custom subclasses. P2 supplies this implementation and the MAC validation call before it enables the HT compatibility checks. The receiver checks the exact MAC capability inputs without a probability call or random draw. It calls the probability operations only for aggregates. An absent model follows the explicit error-free case.
+
+Deterministic tests supply probabilities of zero or one through the same role. The real receiver still produces and caches the outcomes. Those tests establish receiver integration and selective recovery. They do not establish the full NIST/Yans profile.
+
+The shared NIST/Yans implementation uses independent delimiter and MPDU outcomes under the current scalar SNIR reduction. This is a packet-level abstraction. It does not model correlated FEC errors or reconstruct interference within each MPDU. D2 extends the evidence for that implementation before completion of the documented profile.
+
+The decoder checks lengths, alignment, delimiter CRC, and signature before it trusts a member boundary. It can recover a later valid delimiter after corruption. That recovery does not erase the earlier continuity break.
+
+The decoder creates a separate Packet for each complete recoverable member. A failed PHY range sets only that member's bit-error flag. Rx rejects the flag before any FCS-mode branch. Other valid members retain their own outcomes and intact shared chunks. A valid FCS cannot override the member's error flag. [Receive validation](ieee80211-ampdu-implicit-blockack.md#45-receive-validation-and-one-response-per-ppdu).
+
+Rx reports one complete aggregate outcome for contention after all member checks. A correctly received member suppresses EIFS, also when that member addresses another station or lacks an agreement. A delivered container with no correct member invokes the existing corruption procedure once. Separate-part header failure retains the earlier radio abort. An individual member failure must not invoke HCF's ordinary corruption callback or erase valid aggregate history. The PHY's WHOLE summary cannot replace this MAC decision.
+
+## Why receipt and delivery need separate state
+
+Receipt state answers whether an MPDU arrived correctly. The reorder buffer answers when the recipient can deliver its contents. A positive Block Ack bit does not prove immediate upper delivery. Conversely, delivery must not erase receipt evidence that a later Block Ack needs.
+
+```mermaid
+flowchart TD
+ V["Valid received MPDU"] --> S["Receipt record
Preserve its receipt bit"]
+ V --> R["Reorder buffer
Retain data until a release rule applies"]
+ S --> BA["Compressed Block Ack bitmap"]
+ S --> CHECK["Evaluate release conditions"]
+ R --> CHECK
+ FACTS["Member position
Delimiter continuity
Previous PPDU or response history"] --> CHECK
+ CHECK -->|Release required| U["Deliver eligible MSDUs once"]
+ CHECK -->|No release condition holds| HOLD["Retain buffered data"]
+ U -.-> KEEP["Delivery preserves receipt evidence
until scoreboard retirement"]
+```
+
+Aggregate release rules need more information than S5's individual-MPDU interframe check. The reorder service needs member position, delimiter continuity, prior receipt evidence, and the applicable physical interval. HCF supplies those facts through copied timing history. The reorder service releases eligible data when an applicable condition requires that action. [Reorder release and history](ieee80211-ampdu-implicit-blockack.md#46-reorder-release-and-physical-history).
+
+The hypothetical loss of 101 does not imply that every later MPDU must remain buffered. Clause 10.25.6.6.2.1 requires release when an applicable condition in (a)(2)(i–iv) holds. Conservative gap retention cannot replace that action. Tests must prove each reachable condition and its required release action. Sequence arithmetic must also remain correct across 4095 and 0.
+
+## How timing constrains an aggregate
+
+HCF checks the complete exchange: optional RTS/CTS, required interframe spaces, the DATA PPDU, and Block Ack. The actual selected mode and delimiter padding determine DATA airtime. All members carry the same Duration/ID value, referenced to PPDU end. The PHY mode calculation remains the authority for airtime.
+
+RTS selection uses the complete PSDU length, including members, delimiters, alignment, and spacing. Clause 10.3.5 requires RTS/CTS when that length exceeds the threshold. The current policy also selects protection at equality, which the clause permits. HCF supplies an exact aggregate preview and a separate valid member header to that policy. Every batch reduction reevaluates protection and complete-cost admission at the same retained DATA mode. The preview reaches no radio and establishes no commitment or attempt.
+
+In a hypothetical case, a retained retry and a new preview each contain a complete 1500-octet MPDU. With zero extra spacing, the PSDU measures 3008 octets and exceeds a 2346-octet RTS threshold. HCF therefore selects RTS/CTS although each member falls below the threshold. If CTS does not arrive, HCF records one contention failure and releases the previews. The queued source stays queued; the retained member keeps its retry state and deadline. [RTS selection and recovery](ieee80211-ampdu-implicit-blockack.md#47-originator-exchange-timing-and-recovery).
+
+For a hypothetical later exchange, assume 200 microseconds remain in the TXOP. If the complete aggregate exchange needs 240 microseconds, HCF must reduce the batch or defer it. The source MSDUs remain available for a later attempt. A positive amount of time left does not prove that the exchange fits.
+
+| Condition | Proposed result |
+| --- | --- |
+| TXOP limit is zero. | Permit one A-MPDU DATA PPDU with its required protection and acknowledgment. Do not add a second DATA PPDU. |
+| TXOP limit is positive and the aggregate contains multiple MPDUs. | The complete exchange must fit, even when every member is a retry. |
+| A singleton retry can exceed the limit. | Apply the same-size retry exception only when its full conditions and TXOP history permit it. |
+| An initial singleton carries an ordinary MSDU under the agreement. | Apply its separate exception only when the TXOP conditions permit it. A first-attempt A-MSDU cannot use that exception. |
+| HT mixed-format duration reaches its L-SIG limit. | Require calculated `L_LENGTH` at or below 4095. For this profile, `TXTIME - SignalExtension` must not exceed 5484 microseconds. |
+| HT PPDU duration reaches its separate PHY limit. | Also apply the HT limit of 10 ms independently of the L-SIG and TXOP rules. |
+
+For a hypothetical 5 GHz aggregate that needs 6 ms, the 10 ms check passes but calculated `L_LENGTH` reaches 4482. HCF must reduce the batch or defer it. The signal extension changes the corresponding total-duration boundary in 2.4 GHz. [HT duration conditions](ieee80211-ampdu-implicit-blockack.md#42-eligibility-and-limits).
+
+The first profile uses legal non-HT RTS/CTS and non-HT OFDM or ERP-OFDM Block Ack responses. The recipient first selects the highest permitted BSS basic rate at or below the actual HT PPDU's non-HT reference rate. If none qualifies, it selects the highest permitted mandatory rate at or below that same reference rate. A legal alternate must preserve response duration and modulation class. HCF must reject a forced incompatible control format before aggregate submission.
+
+The response deadline starts at PHY transmission completion. It covers SIFS, one slot, and the PHY receive-start delay. Once an eligible reception starts, HCF waits for reception end and validates the response. It does not replace that procedure with an arbitrary whole-response delay. [Timing and response rules](ieee80211-ampdu-implicit-blockack.md#47-originator-exchange-timing-and-recovery).
+
+## What happens when the exchange fails
+
+One aggregate produces one contention outcome, but it can produce different delivery outcomes for its members. A valid matched Block Ack completes the response exchange even when its bitmap contains no positive bits. Members without positive acknowledgment still need retry. An absent or invalid response produces one contention failure.
+
+| Event | Result for the proposed path |
+| --- | --- |
+| A valid bitmap acknowledges some members. | Release those members. Retain the other members with their original deadlines. |
+| A valid bitmap acknowledges no members. | Complete the response exchange. Retain unresolved DATA for retry. |
+| The implicit wait ends without a valid matched Block Ack. | Report one response-exchange failure. End the sequence and arm one explicit BAR probe before further DATA for that agreement. |
+| DATA lifetime expires. | Dispose of the affected member through its current owner. Do not restart its deadline through another aggregate. |
+| Valid addressed Implicit BAR DATA arrives without an agreement. | Discard the DATA. Send no Block Ack or individual ACK. That DATA triggers no DELBA or receive state. |
+| A valid addressed implicit aggregate conflicts with an active Basic agreement. | Report the profile conflict before receipt or reorder updates. Do not silently send a Basic response. |
+
+Silent discard is the first profile's explicit policy choice. Clause 11.5.4 names Block Ack-policy DATA in its absent-agreement DELBA rule. This plan does not extend that rule to Implicit BAR DATA. Current explicit BAR and inactivity recovery retain their separate triggers. [Receive validation and absent-agreement recovery](ieee80211-ampdu-implicit-blockack.md#45-receive-validation-and-one-response-per-ppdu).
+
+Silence gives the originator no immediate agreement-loss notice. Finite batches already have a recovery trigger. After the last DATA expires, HCF checks transfer completion at a safe exchange boundary. It retires the accepted agreement when that peer/TID has no queued or in-progress DATA. This works with inactivity timeout zero.
+
+Continuous arrivals can keep DATA queued. The first failed implicit response wait therefore sets one boolean probe requirement in the current originator agreement. The BAR policy selects one real explicit Compressed BAR before further DATA for that agreement. Admission refusal preserves the flag. Actual submission consumes it. Current EDCA classification derives the probe's access category from the agreement's TID; no category is stored with the flag.
+
+The probe is INET policy beyond the standard's requirements. Clause 10.25.6.7.1 also permits direct DATA retransmission after a lost Block Ack. A submitted probe adds BAR airtime, channel access delay, and a response exchange or timeout. This cost also applies when the agreement remains healthy. Transfer completion can retire an empty agreement before probe submission.
+
+A valid probe response applies the current bitmap rules. Probe failure returns to DATA retry if the agreement remains active. It does not arm another probe by itself. For each agreement, at most one submitted probe occurs between DATA attempts. A valid zero-bit implicit Block Ack does not arm a probe. The probe adds no DATA attempt or duplicate member feedback.
+
+In this hypothetical case, fresh DATA continues to arrive, but the recipient lacks the agreement. It silently discards an implicit aggregate. The failed wait causes an explicit probe. The recipient answers that BAR with DELBA under S5's current rule. The originator retires its stale agreement and can establish a later agreement without queue drain.
+
+```mermaid
+flowchart TD
+ FAIL["Implicit wait ends
No valid matched Block Ack"] --> ARM["Set one probe flag on the agreement
End the sequence and return to contention"]
+ ARM --> EMPTY{"Does this peer/TID
have queued or in-progress DATA?"}
+ EMPTY -->|No| DONE["Current transfer completion
Retire agreement and send DELBA"]
+ EMPTY -->|Yes| BAR["At the next permitted access
Submit one explicit Compressed BAR"]
+ BAR --> OUT{"What occurs after the probe?"}
+ OUT -->|Recipient DELBA| RETIRE["Current lifecycle handler
retires the stale agreement"]
+ OUT -->|Valid Block Ack| BITMAP["Apply transmitted-only bitmap
Permit normal DATA eligibility"]
+ OUT -->|No valid matched response| RETRY["Current BAR control recovery
Permit DATA retry before another probe"]
+ RETIRE --> SETUP["Current setup procedures
can establish a later agreement"]
+```
+
+The diagram describes the proposed recovery policy. The probe waits for Block Ack. DELBA uses the current Action path and can arrive after that wait. It still retires the agreement.
+
+A6 checks finite batches and continuous arrivals with zero and nonzero inactivity values. The finite case forces probe failure so DATA expiry reaches the current completion trigger. The continuous case verifies an actual BAR, recipient DELBA, and later setup while fresh DATA remains queued. [Recovery transitions and checks](ieee80211-ampdu-implicit-blockack.md#appendix-c-recovery-transitions-and-acceptance-cases).
+
+A separate A6 case keeps a healthy agreement active while selected implicit Block Ack responses fail. Full, partial, and absent probe responses must permit actual subsequent DATA transmission before another probe. Original deadlines, per-member feedback, and exactly-once upper delivery remain required. This controlled case establishes no general fairness or goodput bound.
+
+With an active agreement, valid Implicit BAR DATA refreshes recipient inactivity. A duplicate can refresh inactivity without another delivery. Invalid members cannot refresh it. The originator accepts positive bitmap bits only for applicable transmitted MPDUs.
+
+The originator validates received responses against the active receive interval, peer, TID, profile, and frame fields. Frames outside that interval and callbacks from retired local contexts cannot complete the wait. Compressed Block Ack has no identifier for an individual exchange. A hypothetical delayed frame with identical fields inside a later valid interval follows ordinary response validation. The plan promises no rejection based on a hidden link to an earlier exchange. Supported peers send their immediate responses after the required SIFS.
+
+Recovery and observations report each attempted member's actual outcome. The first aggregate transmit profile has no rate-control submodule, so member results cause no rate-control callbacks. Contention counters must not increase once for every member of the same failed exchange. Block Ack DATA retries remain bounded by the current lifetime policy; the plan adds no new member retry limit. General stop/start recovery remains outside this work.
+
+## How the work reaches a complete feature
+
+Two linked plans separate the receiver prerequisite from the MAC feature. The PHY plan owns representation, bounded delimiter parsing, cached outcomes, and error-model integration. The feature plan owns selection, independent member state, reception, mandatory release, responses, and recovery.
+
+D1 is the PHY plan's receiver integration gate. It includes deterministic reception evidence, the shared NIST/Yans implementation, and basic compatibility checks. Feature A2 accepts copied PHY indications and refusal behavior; A4 supplies MAC reception and required release. A5 connects complete exchanges, with the short-GI response check, and A6 verifies negotiation and recovery. None of those steps can omit A4. [Feature steps](ieee80211-ampdu-implicit-blockack.md#6-implementation-steps-and-dependencies).
+
+D2 is the PHY plan's broader NIST/Yans and compatibility evidence gate. Those checks can follow deterministic MAC integration because D1 already includes the implementation and preserves default HT operation. Feature A7 needs both A6 and D2 before the supported profile reaches completion. This split gives each delivery a clear evidence boundary. [PHY steps and gates](ieee80211-ampdu-phy-reception.md#6-implementation-steps-and-dependencies).
+
+```mermaid
+flowchart TD
+ MERGE["Merged #1343, #1344, #1345
Refresh the implementation base"] --> P1
+ P1["PHY P1
Representation and bounded ranges"] --> D1
+ P1 --> PREP["Feature A1 and A3
Capabilities and member preparation"]
+ D1["PHY P2 / D1
Receiver integration, shared models,
and basic compatibility"] --> ACCEPT
+ PREP --> ACCEPT["Feature A2 and A4
Accept outcomes and apply mandatory release"]
+ ACCEPT --> A5["Feature A5 and A6
Complete exchanges, negotiation, and recovery"]
+ D1 --> D2["PHY P3 and P4 / D2
NIST/Yans and compatibility evidence"]
+ A5 --> A7["Feature A7
Document and verify the supported profile"]
+ D2 --> A7
+```
+
+The two plans now contain ten fixtures across unit, module, and protocol tests. The PHY plan owns three implemented fixtures; the feature plan owns seven proposed fixtures. P4 adds the parent-compatible ordinary reception fixture. The complete feature still needs source ownership, reorder release, exchanges, recovery, timing, and production negotiation checks. Current Basic, explicit Compressed, lifetime, reset, and disabled-feature checks provide regression coverage. Compatible configurations with disabled transmit aggregation must preserve current frame sequences and random draws. [Verification methods](ieee80211-ampdu-implicit-blockack.md#7-verification-methods-and-expected-results).
+
+Direct exchange assertions must prove one DATA PPDU, one response after SIFS, correct receipt bits, selective retry, and exactly-once upper delivery. Physical reception tests must produce the partial results through the real receiver. An injected bitmap cannot prove that PHY integration. Fingerprints can detect a changed trajectory; they cannot replace these assertions.
+
+The planned campaign also checks member flags under declared-correct and computed FCS modes. It covers 2.4 GHz and 5 GHz, HT MCS 0 and 7, legal response modes, and supported protection combinations. Boundary checks cover sequence wrap, buffer sizes 1, 2, and 64, expiration order, and independent peers and TIDs. Deterministic loss cases prove the causal paths. The small additional seed campaign establishes no statistical goodput claim.
+
+The corrected checks also cover production member FCS before and after selective retry, partial-reception EIFS suppression, and agreement removal during SIFS. Admission checks distinguish the HT mixed-format L-SIG condition from the separate 10 ms limit. A nonzero-spacing case proves that batch reduction retains one DATA rate decision.
+
+RTS checks use complete PSDU lengths below, equal to, and above the configured threshold. Spacing and batch reduction must cause renewed protection selection. Recovery checks cover mixed batches, numbered unsent members, internal collision survivors, and terminal collision disposal. Repeated aggregate RTS failure reaches the contention retry boundary without member retry increments, member disposal, DATA outcomes, or a pending probe. Probe checks derive ownership from the agreement's TID after member expiry. These cases remain proposed verification.
+
+PHY checks also prove HT-SIG golden bytes and independent decode, PREAMBLE query order, and the two failed-header delivery paths. Invalid CRC and unsupported fields cause HEADER failure with successful header probabilities and without an error model. A separate malformed-input case retains HEADER success but refuses insufficient modeled PSDU content at DATA. Metadata checks verify the aggregate probability fields and the applicable receive tags without another probability call or random draw. The PHY fixtures own these cases. None of these checks establishes physical carrier-loss behavior.
+
+The proposed MAC checks reject aggregate transmit opt-in with AARF. Proposed response checks separate observable mismatches and retired callbacks from indistinguishable valid frames inside an active interval. MAC FCS acceptance remains pending.
+
+Completed PHY checks reject absent aggregate roles and incomplete advertised receive-mode coverage before the MIB update. The PHY controls preserve ordinary HT reception with NIST, Yans, and an absent error model. Aggregate checks without an error model retain common PHY checks without new error draws. The PHY execution record states the direct evidence and its limits.
+
+A fixture peer supplies a valid HT20 short-GI aggregate through the real radio. D1 and A2 verify copied PHY indications without MAC receipt or response requirements. After A4 supplies member validation, A5 verifies MAC receipt and one Compressed response after SIFS. A6 verifies production capability negotiation with the local aggregate transmit policy at long GI. This receive case does not establish short-GI selection from production transmit queues. [Receive acceptance cases](ieee80211-ampdu-implicit-blockack.md#proposed-direct-checks).
+
+## What the first version leaves for later
+
+The first transmit policy selects ordinary MSDU members. It does not add nested A-MSDU selection. Current individual A-MSDU operation remains available, and reception preserves the current negotiated A-MSDU checks and deaggregation.
+
+The first version excludes these larger features:
+
+- VHT PPDUs, HT40, HE/EHT, Multi-TID, and multi-user aggregates.
+- Short-GI aggregate transmission through the new HT policy.
+- Delayed Block Ack, partial-state scoreboards, protected agreements, dynamic fragmentation, and group-addressed aggregates.
+- Reverse direction, mixed control/data aggregates, adaptive aggregate transmission, new rate-control algorithms, and general MAC stop/start recovery.
+- Bit-level FEC behavior, correlated error accuracy, errors confined to alignment padding, and a guaranteed performance gain.
+
+Custom data services, MPDU policies, Tx/Rx modules, response policies, and response-rate selectors can need interface updates. The DATA rate-control API remains unchanged; aggregate transmit opt-in requires its HCF submodule to be absent. Present custom HT models need the optional PHY role with coverage of advertised receive modes. This includes configurations that previously used only ordinary MPDUs. Non-HT custom reception and operation without an error model remain available without that role. The implementation must document those obligations.
+
+The design reuses the public Packet API and proposes no edit to its sealed implementation. Related feature branches remain separate integration work. [Compatibility and scope](ieee80211-ampdu-implicit-blockack.md#5-affected-components-and-compatibility).
+
+The user's approved revision covers these documents only. It adds no implementation approval or runtime evidence. The two linked plans own the detailed decisions, source references, standard clauses, commands, and verification limits.
diff --git a/plan/pending/ieee80211-ampdu-implicit-blockack.md b/plan/pending/ieee80211-ampdu-implicit-blockack.md
new file mode 100644
index 00000000000..2a95af3b58f
--- /dev/null
+++ b/plan/pending/ieee80211-ampdu-implicit-blockack.md
@@ -0,0 +1,639 @@
+# Implementation plan: A-MPDU with implicit Compressed Block Ack
+
+> **Kind:** procedure · **Status:** draft · **Seal:** none · **Owns:** — · **Stands on:** [PHY prerequisite plan](ieee80211-ampdu-phy-reception.md), [S5 execution record](ieee80211-compressed-blockack-s5-execution.md), [implementation plan procedure](../../doc/project/guide/write-an-implementation-plan.md), [IEEE 802.11 rules](../../doc/project/domain/ieee80211.md)
+
+Checkout: `/home/user/omnetpp_ws/inet-compressed-blockack-s5`, branch `work/compressed-blockack-s5`, HEAD `4fa5cc0de15ee57388bcb3f20e866cab5972a670`.
+
+Plan revision: 12, 2026-10-17. The user's `proceed, including the optional simplification` authorizes the reviewed document corrections. This revision defines PSDU-based RTS selection, recovery before DATA submission, and a boolean probe requirement with a derived access category. This document accompanies PHY revision 11 and the revised summary. The delivery gates and PHY behavior retain their current scope. Implementation, expectation changes, commits, and publication remain outside this authorization.
+
+The project plan procedure remains a draft; this document uses its structure without an additional approval requirement.
+
+This feature plan consumes the [PHY prerequisite plan](ieee80211-ampdu-phy-reception.md). That plan owns aggregate representation, bounded delimiter parsing, cached partial reception, and error-model integration. This plan owns member selection, MAC reception, mandatory release, implicit responses, and recovery.
+
+Implementation waits for merged PRs [#1343](https://github.com/inet-framework/inet/pull/1343), [#1344](https://github.com/inet-framework/inet/pull/1344), and [#1345](https://github.com/inet-framework/inet/pull/1345). The assessment on 2026-10-16 confirmed all three as open. Read-only A0 checks can occur before merge. A0 cannot close until the merged dependency chain and refreshed implementation base exist.
+
+The [delivery table](#6-implementation-steps-and-dependencies) identifies the stages and their prerequisites. The [fixture table](#7-verification-methods-and-expected-results) identifies acceptance evidence. Detailed standards and execution commands remain in the appendices.
+
+## 1. Problem and intended behavior
+
+The proposed feature sends multiple QoS MPDUs in one HT PPDU. The recipient returns one Compressed Block Ack after SIFS. The aggregate itself requests that response, so the successful exchange needs no separate BAR. Each MPDU retains its own sequence number, FCS, lifetime, and retry state.
+
+S5 supplies explicit Compressed BAR exchanges for individual MPDUs. It does not connect A-MPDU selection, aggregate reception, or the implicit response sequence. The current aggregate builder alone cannot provide those mechanisms. This plan adds the complete path through production queues, PHY reception, and acknowledgment recovery.
+
+This hypothetical case defines the main acceptance scenario. An immediate Compressed agreement exists for one peer and TID, with a transmit window that starts at 100. Four queued MSDUs become MPDUs 100 through 103 in one A-MPDU. The recipient receives 100, 102, and 103 correctly; MPDU 101 fails its FCS check. The recipient sends one Compressed Block Ack after the PPDU ends. The bitmap reports 100, 102, and 103 as received.
+
+The originator retains 101 for retry and releases the other transmitted MPDUs. A singleton A-MPDU can carry the retry of 101 and request another Block Ack. Each MSDU reaches the upper protocol exactly once. The reorder service applies every release condition that the supported aggregate path requires; an applicable release condition requires delivery across a gap.
+
+```mermaid
+sequenceDiagram
+ participant Q as Originator queue and HCF
+ participant P as Recipient PHY and HCF
+ participant U as Recipient upper protocol
+ Note over Q,P: Accepted immediate Compressed agreement for one peer and TID
+ Q->>P: One HT PPDU: MPDUs 100, 101, 102, 103
+ Note over P: 101 fails. Record valid MPDUs and apply reorder rules.
+ P->>Q: After SIFS: Compressed Block Ack, bits 100=1, 101=0, 102=1, 103=1
+ Note over Q: Release 100, 102, 103. Retain 101 with its original deadline.
+ Q->>P: Singleton A-MPDU: retry 101
+ P->>Q: After SIFS: Compressed Block Ack
+ P->>U: Deliver each eligible MSDU once
+```
+
+The diagram describes proposed behavior. It is not an executed trace.
+
+## 2. Current implementation and evidence
+
+The facts below come from the pinned source. The [S5 record](ieee80211-compressed-blockack-s5-execution.md) supplies historical verification; this plan supplies no new runtime result.
+
+| Component and source | Confirmed behavior | Required consequence |
+| --- | --- | --- |
+| [QoS data service](../../src/inet/linklayer/ieee80211/mac/originator/OriginatorQosMacDataService.cc) | Lines 309–313 contain the commented A-MPDU invocation. Reversible preparation currently produces one MPDU or A-MSDU. | Extend preparation to a batch without premature queue extraction. |
+| [Default MPDU policy](../../src/inet/linklayer/ieee80211/mac/aggregation/BasicMpduAggregationPolicy.cc) | The policy always returns `nullptr`. | Add an explicit HT policy; preserve the current default. |
+| [Aggregate builder](../../src/inet/linklayer/ieee80211/mac/aggregation/MpduAggregation.cc) | The helper concatenates MPDUs with delimiters and alignment padding. It deletes its inputs. | Retain canonical MPDUs separately for selective retry. |
+| [Aggregate decoder](../../src/inet/linklayer/ieee80211/mac/aggregation/MpduDeaggregation.cc) | The helper assumes valid lengths and derives names from the aggregate name. | Consume bounded parsing from the PHY plan. Apply MAC validation without name-dependent behavior. |
+| [HT capability state](../../src/inet/linklayer/ieee80211/mib/Ieee80211HtCapabilities.h) | Directional state retains the receiver's maximum A-MPDU length exponent. It omits minimum MPDU start spacing. | Carry spacing through local advertisement, peer state, and directional negotiation. |
+| [Management conversion](../../src/inet/linklayer/ieee80211/mgmt/Ieee80211HtMgmtElements.h) | The wire element contains spacing, but conversion does not retain it. | Preserve all supported spacing values through real management paths. |
+| [MAC receive entry](../../src/inet/linklayer/ieee80211/mac/Ieee80211Mac.cc) and [Rx](../../src/inet/linklayer/ieee80211/mac/Rx.cc) | Rx expects one MAC trailer and one MAC header. | Recognize an aggregate before the ordinary MPDU FCS and header path. |
+| [Tx](../../src/inet/linklayer/ieee80211/mac/Tx.cc) and [its contract](../../src/inet/linklayer/ieee80211/mac/contract/ITx.h) | Tx finalizes one MPDU before SIFS, retains a copy, and sends that copy when its timer expires. | Add aggregate submission at the actual start, with per-member finalization and an explicit cancellation result. |
+| [RTS policy](../../src/inet/linklayer/ieee80211/mac/originator/QosRtsPolicy.cc) and [its contract](../../src/inet/linklayer/ieee80211/mac/contract/IRtsPolicy.h) | The policy reads the supplied Packet's length and a separate MAC header. Its current threshold comparison includes equality. | Supply the complete aggregate preview and a valid member header. Reevaluate protection after batch reduction. |
+| [HCF recovery before DATA](../../src/inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.cc) | Internal collision recovery can number an unsent frame. RTS failure updates only contention for an uncommitted preview; committed frames also receive member retry accounting. | Include numbered, unsent candidates. Define aggregate RTS recovery independently of one representative member's state. |
+| [Rx failure processing](../../src/inet/linklayer/ieee80211/mac/Rx.cc) and [HCF corruption callback](../../src/inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.cc) | Ordinary rejection notifies contention. The MAC also invokes HCF's corruption callback, which clears interframe history. | Separate member rejection from the complete aggregate outcome for contention and history. |
+| [DATA rate selection](../../src/inet/linklayer/ieee80211/mac/rateselection/QosRateSelection.cc) and [rate control](../../src/inet/linklayer/ieee80211/mac/contract/IRateControl.h) | Current rate control selects a rate by recipient address. It takes no aggregate size. | Select one mode before mode-dependent spacing. Reuse the current recipient-based decision. |
+| [AARF feedback](../../src/inet/linklayer/ieee80211/mac/ratecontrol/AarfRateControl.cc) | Each callback can change the selected rate and probe state. The callback contains no actual transmission mode. | Separate callbacks for one aggregate can describe a probe at a rate that no member used. Restrict the first transmit profile to fixed rates. |
+| [PHY receiver](../../src/inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211Receiver.cc) and [error model](../../src/inet/physicallayer/wireless/ieee80211/packetlevel/errormodel/Ieee80211ErrorModelBase.cc) | The receiver obtains a whole-data success decision. The WLAN error model supports whole-Packet corruption only. | Consume cached delimiter and MPDU outcomes from the PHY plan. |
+| [HT PHY header](../../src/inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211PhyHeader.msg) | The modeled HT header has no aggregation field. | Consume the PHY plan's HT aggregation property for both singleton and multiple-member aggregates. |
+| [Frame sequence](../../src/inet/linklayer/ieee80211/mac/framesequence/TxOpFs.cc) and [acknowledgment handler](../../src/inet/linklayer/ieee80211/mac/originator/QosAckHandler.cc) | `NORMAL_ACK` selects an ACK wait. An explicit BAR creates the current Block Ack wait. | Add an aggregate exchange with a Block Ack wait and no synthetic BAR. |
+| [Reorder service](../../src/inet/linklayer/ieee80211/mac/blockackreordering/BlockAckReordering.cc) and [HCF history](../../src/inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.h) | S5 supplies individual-MPDU interframe evidence. The reorder call accepts one interframe boolean. | Supply aggregate position, delimiter continuity, and previous PPDU facts for aggregate release rules. |
+| [HCF transfer completion](../../src/inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.cc) | Lifetime expiry and exchange completion invoke `completeTransfers()`. An accepted agreement with transmitted DATA retires when its peer/TID has no queued or numbered DATA. | Name this finite-batch recovery trigger. Preserve transmitted-member records for aggregate submissions. |
+| [QosAckHandler](../../src/inet/linklayer/ieee80211/mac/originator/QosAckHandler.cc) and [BAR policy](../../src/inet/linklayer/ieee80211/mac/originator/OriginatorQosAckPolicy.cc) | Current BAR selection uses `BLOCK_ACK_NOT_YET_REQUESTED` members. It does not select failed implicit waits automatically. | Define an explicit probe trigger that also works while fresh DATA remains queued. |
+
+Prior local edits affect `AGENTS.md`, the replacement plan, its summary, the agreement lifecycle plan, and the EDCA lifetime plan. The S5 summary and planning history also exist as untracked files. This plan does not alter those files. The source revision above remains the implementation reference; local prose does not establish additional runtime support.
+
+## 3. Scope and acceptance criteria
+
+### First supported profile
+
+The first aggregate transmit profile targets packet-level `Ieee80211Radio` with scalar or dimensional reception, HT mixed format, 20 MHz, and long GI. Aggregate transmission requires a fixed DATA rate and no HCF rate-control submodule. The transmitter must select an actual HT mode. An `ac` interface can qualify only when its actual transmission uses this HT profile and valid peer HT state. This does not add VHT PPDU support.
+
+Reception also covers HT20 short GI when local HT capability advertises that receive mode. The PHY contract requires coverage of the advertised MCS and NSS values within the effective radio limits. Interface presence alone cannot establish that coverage. The MAC supplies its exact capability inputs to the receiver for validation before the MIB update. This receive requirement does not extend the new aggregate transmit policy to short GI. [PHY receive contract](ieee80211-ampdu-phy-reception.md#42-optional-role-and-shared-implementation).
+
+Each aggregate contains unfragmented, individually addressed QoS Data MPDUs for one RA, TA, and TID. An accepted immediate Compressed agreement must exist. Multiple peers and agreements can coexist; each aggregate remains within one agreement. The first transmit policy forms ordinary MSDU members. New selection of nested A-MSDUs remains separate work; current individual A-MSDU operation remains available.
+
+The recipient must preserve the current negotiated A-MSDU checks and deaggregation when it accepts an otherwise valid received member. The new transmitter must not use nested A-MSDU selection to bypass an absent peer length limit. An A-MSDU retry from S5 can retain the individual path.
+
+A-MPDU transmission uses a new `HtMpduAggregationPolicy` through the current optional `mpduAggregationPolicy` slot. The default `BasicMpduAggregationPolicy` retains its no-aggregation behavior. The receiver recognizes supported aggregates independently of its own transmit policy. No new on-wire capability bit or Block Ack profile selector represents this local choice.
+
+When no aggregate qualifies, use the current individual-frame path where its conditions permit transmission. Absent peer HT state retains S5's Normal Ack fallback. An enabled policy does not grant an agreement or override a PHY limit.
+
+The PHY plan rejects a present HT model without the aggregate role or required receive-mode coverage before management advertises capabilities. This check applies independently of local transmit aggregation. An absent error model retains the current error-free abstraction after the required PHY checks. The PHY plan's [compatibility section](ieee80211-ampdu-phy-reception.md#5-affected-components-and-compatibility) defines custom-model migration and the supported absent-model case.
+
+This work remains outside this plan:
+
+- VHT PPDUs, HT40, HE/EHT, Multi-TID, and multi-user aggregates.
+- Short-GI aggregate transmission through the new HT policy.
+- Delayed Block Ack, partial-state scoreboards, protected agreements, and dynamic fragmentation.
+- Group-addressed aggregates, reverse direction, and mixed control/data aggregates.
+- New stop/start recovery, general TXOP repair outside this feature, adaptive aggregate transmission, and rate-control algorithms.
+- Bit-level FEC simulation, time-resolved error correlation, and a performance improvement guarantee.
+
+Current explicit Compressed and Basic exchanges remain supported. A valid explicit BAR still receives its current response. The new feature preserves the S5 profile conflict diagnostic and finite QoS DATA lifetime.
+
+### Observable completion
+
+1. Production association establishes the HT limits and immediate agreement before aggregate transmission.
+2. One HT PPDU carries at least two independently identifiable MPDUs from production queues.
+3. A valid member with ACK policy bits `00` causes one Compressed Block Ack after the complete PPDU and SIFS.
+4. The successful implicit exchange contains no BAR and no individual ACK for its members.
+5. Partial loss produces correct receipt bits and selective retry without duplicate delivery.
+6. A singleton aggregate retry still receives Block Ack; MPDU count does not change its response kind.
+7. Refusal, RTS failure, expiration, and agreement removal preserve or dispose of each owned frame through its current owner.
+8. Aggregate length, member length, spacing, transmit window, PPDU duration, and TXOP conditions hold at actual transmission.
+9. Compatible feature-off cases preserve current sequences and random draws. Separate peers and TIDs retain independent state.
+10. Absent-agreement Implicit BAR DATA causes discard without a response to that DATA. Finite and continuous traffic cases prove recovery and later setup.
+11. Deterministic integration and NIST/Yans support have separate evidence gates. Completion of the documented profile requires the PHY plan's D2 gate.
+12. A failed implicit response wait arms one explicit BAR probe before further DATA for that agreement. Valid zero-bit Block Ack does not arm a probe. One boolean retains the requirement; current EDCA classification derives its access category from the agreement's TID.
+13. Admission checks the L-SIG length condition for HT mixed format. The separate 10 ms HT duration limit also applies.
+14. Production transmission calculates each member's FCS after its final header fields, including changes for selective retry.
+15. A correctly received aggregate member prevents EIFS, an access delay after specified reception errors. This also applies to valid overheard members.
+16. Agreement removal during SIFS cancels an unsent aggregate before queued-source commitment, new sequence assignment, or DATA attempt accounting. Existing canonical members retain their assigned numbers unless current disposal rules remove them.
+17. Aggregate transmit opt-in with a rate-control submodule fails initialization, even when a fixed-rate override would bypass its rate selection.
+18. Present HT models without the aggregate role or required receive-mode coverage fail before capability advertisement, also with local transmit aggregation disabled.
+19. Response acceptance uses the active receive interval and observable frame fields. Retired local callbacks cannot complete another wait.
+20. An absent error model preserves current PHY checks and adds no error draws. PHY D1 preserves default NIST and configured Yans operation.
+21. The modeled HT-SIG header preserves HT Length and Aggregation through serialization and independent decode.
+22. PREAMBLE queries add no early HEADER or DATA calculation. Failed-header delivery follows the radio's actual reception mode.
+23. Aggregate results preserve applicable receive metadata and use the PHY plan's explicit probability contract.
+24. Advertised HT20 short-GI aggregates reach MAC validation without an extension of the new long-GI transmit policy.
+25. Invalid encoded HT-SIG prevents member outcomes, receipt updates, delivery, and responses through the current header-failure paths.
+26. Insufficient modeled PSDU content causes structural DATA refusal without range draws. The valid HEADER result retains its decoded Aggregation property.
+27. RTS selection uses the complete PSDU length, including delimiters and padding. Batch reduction reevaluates that decision before complete-cost admission.
+28. Numbered, unsent MPDUs remain eligible without another sequence assignment. Aggregate RTS failure produces one contention failure without member retry increments or RTS-triggered member disposal.
+
+## 4. Proposed design and reasons
+
+### 4.1 Requirements that control the design
+
+IEEE Std 802.11-2024 controls the supported HT profile. [Appendix A](#appendix-a-standard-requirements-and-evidence) retains the detailed clauses, canonical corpus IDs, PDF pages, and extraction limits.
+
+The design preserves these conditions:
+
+- The actual HT aggregation property distinguishes a singleton A-MPDU from a standalone MPDU.
+- The receive contract covers advertised HT20 long-GI and short-GI modes independently of the local transmit policy.
+- HT-SIG CRC and field validation precede acceptance of Aggregation, HT Length, and member outcomes.
+- Each accepted member needs valid delimiter context, a successful PHY outcome, and a valid FCS.
+- One eligible aggregate requests one Compressed Block Ack after the complete PPDU and SIFS.
+- Clause 10.25.6.6.2.1 requires release when an applicable condition in (a)(2)(i–iv) holds. Conservative gap retention cannot replace that behavior.
+- Table 19-25 specifies an HT `aPPDUMaxTime` of 10 ms. Clause 10.13 applies that bound independently of TXOP admission.
+- Clause 10.27.4 also requires the calculated HT mixed-format `L_LENGTH` to remain at or below 4095 octets.
+- Clause 10.3.2.3.7 prohibits EIFS when at least one member of an A-MPDU arrives correctly.
+- Clause 10.3.5 requires RTS/CTS when the complete PSDU exceeds the applicable RTS threshold in this HT profile.
+- Clause 10.23.2.12.1 distinguishes ordinary frame retry accounting from Block Ack operation. It separately requires frame retry increments after internal collisions.
+
+The source assessment confirmed the commented A-MPDU call at `OriginatorQosMacDataService.cc:309–313`. PDF inspection confirmed the mandatory release text and the 10 ms table value.
+
+### 4.2 Eligibility and limits
+
+HCF decides whether the proposed exchange qualifies under the active profile, agreement, and actual PHY mode. The HT policy selects eligible members within those supplied limits. Management and the MIB remain the owners of advertised and negotiated capabilities. The policy does not read unrelated module parameters or invent peer capability state.
+
+Add minimum MPDU start spacing to local HT capability state and its directional receiver limits. Preserve its encoded value from 0 through 7. Extend equality checks, management conversion, full association, simplified management, and cleanup through the current capability path. Keep the current zero-spacing default. An asymmetric test must prove that the peer receiver's value controls local transmission.
+
+The aggregate must satisfy all applicable limits:
+
+- Each member's complete MPDU length fits the HT delimiter limit of 4095 octets.
+- The complete PSDU fits the peer maximum and the HT maximum of 65535 octets.
+- Member sequence numbers fit the active transmit window and negotiated buffer capacity.
+- Alignment and zero-length delimiters meet the peer spacing requirement at the retained DATA mode.
+- The selected mode's airtime satisfies the L-SIG length condition, HT PPDU duration bound, and applicable TXOP rule.
+
+The L-SIG field is the legacy signal field in the HT mixed-format preamble. Its `L_LENGTH` value tells non-HT stations when the PPDU ends.
+
+The mode API owns airtime. Extend the HT mode contract to expose both duration conditions for the aggregate admission check. Clause 10.27.4 requires `L_LENGTH = ceil((TXTIME - SignalExtension - 20 us) / 4 us) * 3 - 3` to remain at or below 4095. For the supported long-GI profile, this limits `TXTIME - SignalExtension` to 5484 microseconds. The signal extension is zero in 5 GHz and six microseconds in 2.4 GHz when applicable. Table 19-25's separate 10 ms limit also applies.
+
+In a hypothetical 5 GHz case, an aggregate needs 6 ms and otherwise fits the peer and TXOP limits. Its calculated `L_LENGTH` is 4482, so HCF must reduce the batch or defer it. The 10 ms check alone would accept an illegal HT mixed-format transmission. These are HT requirements; no VHT duration limit supplies either check. The current `getMpduMaxLength()` value does not define the member delimiter limit.
+
+### 4.3 Preparation, commitment, and ownership
+
+Extend the current reversible preparation mechanism to retain a batch of candidate MPDUs. New candidates remain in their queues until commitment. `InProgressFrames` retains committed candidates, including transmitted retries and numbered, unsent MPDUs. Preparation owns previews and copied membership keys; it borrows committed canonical members. It stores no second acknowledgment map or lifetime table.
+
+Retain current EDCAF arbitration and the first eligible peer/TID choice. The HT policy uses the selection order below within one agreement and its current transmit window. This order is an INET policy, not an HT requirement. Current acknowledgment state distinguishes transmitted retries from numbered, unsent candidates.
+
+| Selection order and source state | Preparation and commitment |
+| --- | --- |
+| 1. Eligible transmitted retries, in cyclic sequence order. | Borrow canonical members from `InProgressFrames`. Preserve their sequence numbers and original deadlines. Derive the Retry bit from current acknowledgment state. |
+| 2. Eligible numbered, unsent MPDUs, in cyclic sequence order. | Borrow canonical members from `InProgressFrames`. Preserve their assigned numbers, recovery state, and deadlines. Keep Retry clear before their first DATA submission. |
+| 3. Eligible new members, in queue order. | Retain source MSDUs in their queues. Previews use consecutive candidate numbers without counter advancement. Commitment verifies that reference before actual assignment. |
+
+The current internal collision handler can extract and number one frame before any DATA transmission. Preserve that single-frame recovery path, including its counter update and terminal disposal. It must not prepare or commit an aggregate for the EDCAF that loses the collision. A frame that remains after collision recovery retains `FRAME_NOT_YET_TRANSMITTED` in the acknowledgment handler. Aggregate preparation accepts it through the second row above without another allocation.
+
+In a hypothetical case, an internal collision assigns sequence number 100 to one frame but transmits no DATA. Later aggregate preparation selects that retained frame and a queued new member. Only the new member receives sequence number 101 at DATA submission. Member 100 retains its deadline and collision recovery state. Its first DATA transmission keeps Retry clear.
+
+HCF selects one actual DATA mode through the current fixed-rate DATA rules. Existing interface-wide or per-receiver rate settings supply that choice. HCF supplies the selected member's recipient and header context, without a delimiter envelope or a size-aware rate-control API. The service then calculates delimiter spacing and exact aggregate length at that retained mode. HCF can reduce the proposed batch without a second rate decision. A reduced batch requires another length and airtime calculation, not another rate selection.
+
+HCF rejects aggregate transmit opt-in during initialization when its optional rate-control submodule exists. This includes a controller whose rate selection a fixed-rate override would bypass, because HCF still sends feedback to that controller. AARF can change its probe state after the first member callback. A later callback for the same PPDU can then describe a probe at a rate that no transmission used. Fixed-rate aggregate transmission avoids that false result without a new feedback API or changes to existing rate algorithms.
+
+In a hypothetical AARF case, nine successes precede a two-member aggregate at rate R. The increase threshold is ten, and a faster rate exists. The first member succeeds, so its callback selects the faster rate and sets probe state. The second member fails at R, but its callback treats that failure as a failed probe at the faster rate. The fixed-rate profile rejects this controller configuration before any aggregate attempt.
+
+For a hypothetical peer with nonzero minimum spacing, a faster selected mode can require more zero-length delimiters between short MPDUs. HCF therefore obtains the mode before the service calculates that padding. If the padded batch exceeds a limit, HCF removes candidates and checks the smaller batch at the same mode. The selector receives no claim that the preliminary candidate size equals the final aggregate size.
+
+A new batch normally starts with two members. A batch with a transmitted retry can contain one member and retain aggregate response semantics. A numbered, unsent singleton can use the current individual-frame path. If no batch qualifies, the current individual-frame path can proceed under its own conditions.
+
+Here, DATA submission means the actual call that sends the aggregate from Tx to the radio. Acceptance of a delayed request by Tx does not commit source MSDUs. At actual submission, HCF checks agreement state, mode permissions, candidate membership, and complete-cost admission. The service checks queue identities, sequence references, and deadlines through their current owners. One synchronous commit assigns sequence numbers only to queued new members and transfers them to `InProgressFrames`. The commit preserves all previously assigned numbers and original deadlines.
+
+HCF sets each member's Retry bit, ACK policy, and common Duration/ID before Tx finalizes the member. Tx applies the local TA and configured FCS mode through a shared MPDU finalizer. For computed FCS, Tx calculates each member's FCS over that member's final header and payload. Header or trailer edits use copy-on-write before the new envelope takes references. The aggregate builder then combines those complete MPDUs with delimiters and padding. Tx sends that envelope without an outer MAC header, outer FCS, or another ordinary MPDU finalization pass.
+
+`InProgressFrames` owns canonical committed MPDUs until acknowledgment, expiration, or terminal disposal. The aggregate envelope owns delimiter chunks and references to immutable member content. Envelope destruction releases only its own content references. It does not destroy the canonical MPDUs. Region tags retain the correct member offsets and lengths. Aggregate names do not encode protocol identity.
+
+Extend the aggregate Tx contract with a start callback to HCF and a cancellation result. For delayed submission, Tx invokes that callback at its existing `endIfsTimer` boundary, before it copies or sends the PSDU. With zero IFS, Tx invokes the same callback synchronously. HCF validates the batch before the service commits it and Tx finalizes its members. Tx then submits the complete envelope to the radio in that synchronous call chain. Tx reports actual submission once so HCF can record each transmitted member.
+
+The frame sequence context owns the preparation until submission or cancellation. Agreement retirement marks an unsent preparation as cancelled. Tx clears its pending callback before it reports refusal to the frame sequence handler. Refusal ends the pending aggregate step without DATA failure feedback or an implicit-response probe. Reset must cancel a pending aggregate request through Tx before it releases the context. Ordinary synchronous calls consume no simulation time; this extension adds no timer, zero-delay event, or generation counter.
+
+Tx reports `hasTransmission()` while the aggregate request awaits its start or physical completion. It derives that result from the retained request or transmitted envelope. This preserves the current deferral of radio configuration commands. Commitment also preserves current queue notifications.
+
+In a hypothetical case, an originator inactivity deadline falls between CTS reception and the prepared aggregate's DATA start. The [current inactivity handler](../../src/inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.cc) retires that agreement through `beginTeardown()`. At the existing Tx timer event, HCF refuses the cancelled preparation. No aggregate reaches the radio, and no new source receives a sequence number. Independent expiry or teardown can still dispose of frames through their current owners. Section 4.7 defines aggregate RTS failure without commitment or member retry accounting.
+
+### 4.4 PHY representation and partial reception
+
+The [PHY plan](ieee80211-ampdu-phy-reception.md#4-proposed-design-and-reasons) owns the HT aggregation property and cached delimiter and MPDU outcomes. Its D1 gate supplies deterministic reception through the real radio and receiver. This feature consumes that result without a second error draw. Member count and transmit-only tags cannot replace the decoded HT property.
+
+PHY P1 also owns the modeled HT-SIG fields and serializer. Its wire fixture must prove HT Length and Aggregation through golden bytes and independent decode. A1 consumes that representation for singleton and multiple-member aggregates. The work covers the modeled HT-SIG header without a new waveform model. [HT-SIG representation](ieee80211-ampdu-phy-reception.md#41-representation-and-bounded-ranges).
+
+The radio already declares a 48-bit HT header. PHY P0 records this length and the current mode/PSDU duration path. P1 preserves the declared length through serialization and independent decode. Ordinary HT controls must preserve airtime under identical mode and PSDU inputs. Byte or fingerprint changes require separate evidence; they establish no necessary airtime change.
+
+PHY P1 defines one stateless HT-SIG validator; P2 applies it at the receiver's HEADER boundary. Invalid CRC or unsupported fields prevent delimiter and member draws. MAC receives no accepted Aggregation property from that header. Current header-failure paths prevent receipt updates, delivery, and responses. Encoded-header rejection needs separate evidence from probabilistic header loss. [HT-SIG receive validation](ieee80211-ampdu-phy-reception.md#ht-sig-receive-validation).
+
+The PHY separately bounds valid HT Length against the available modeled PSDU before range calculation or length-based decapsulation. Insufficient content denotes malformed model input. HEADER retains success and its decoded Aggregation property; DATA refuses the content without range outcomes or draws. The radio skips length-based trailer removal and supplies a failure container. MAC refuses that content before header or trailer peeks. This case establishes bounds safety, with no claim about physical carrier loss or acceptance of a received prefix.
+
+PHY P2 defines PREAMBLE, HEADER, DATA, and WHOLE decisions at their actual radio boundaries. PREAMBLE must not construct HEADER or DATA. A2 accepts the mode-specific failed-header paths and applicable receive metadata. The PHY plan owns the aggregate probability fields; MAC must not present an aggregate estimate as a member estimate. [Decision and metadata contracts](ieee80211-ampdu-phy-reception.md#43-decision-ownership-and-random-draws).
+
+PHY P0 must close the audit of aggregate metadata consumers before P2 implements its probability contract. The proposed `NaN` fields follow `ErrorRateInd`'s documented contract for unknown probabilities. A reachable incompatible reader blocks that implementation until the PHY plan records its resolution. Omission requires evidence that all affected readers accept an absent tag. Ordinary MPDU metadata retains its current behavior.
+
+The PHY plan keeps its optional WLAN contract separate from generic `IErrorModel`. Its shared WLAN implementation reuses the current per-length success calculation for NIST, Yans, and compatible custom subclasses. A present custom HT model outside that base also needs the aggregate role, even for ordinary MPDU operation. Its mode-support query must cover advertised HT20 long-GI and short-GI reception without a probability call or random draw. Non-HT custom models retain ordinary MPDU reception without that role.
+
+BerTable remains outside this HT aggregate profile and receives no aggregate role. Its current initializer already rejects HT operation strings before the proposed role check. The PHY plan owns its named compatibility decision, migration text, non-HT parent control, and expected HT refusal case. That current refusal supplies no evidence for the new custom-model diagnostic. [BerTable compatibility](ieee80211-ampdu-phy-reception.md#5-affected-components-and-compatibility).
+
+An absent error model follows the current error-free abstraction after PHY acquisition, possibility, attempt, and SNIR checks. The receiver records successful physical ranges without error-model calls or random draws only after those checks succeed. MAC delimiter, FCS, address, and agreement checks still control acceptance. An absent model needs no substitute role or module.
+
+D1 permits MAC integration and deterministic exchange checks. It includes the shared NIST/Yans implementation before the early HT check becomes active. D2 completes the broader NIST/Yans and compatibility evidence. A7 requires D2 before the feature claims the full NIST/Yans profile. The abstraction uses common PHY acquisition and header success with independent range outcomes under scalar SNIR reduction. It makes no claim about correlated FEC errors or interference within each member.
+
+### 4.5 Receive validation and one response per PPDU
+
+The MAC and Rx recognize aggregate context before the ordinary header and trailer peeks. MAC rejects a delivered failure container after common header failure before those peeks. That failure does not establish a decoded aggregation property. The shared decoder from the PHY plan bounds every delimiter, MPDU length, alignment interval, and zero-length delimiter against the PSDU. Each iteration must advance. Invalid input must cause a bounded refusal or resynchronization, never an out-of-range read.
+
+Separate-part PREAMBLE or HEADER failure aborts the radio's attempted reception before any container reaches MAC. Existing radio state notifications let HCF invalidate the relevant physical history. Whole-signal mode can supply a failure container that MAC must reject. Both paths prevent acceptance, responses, and range draws. Do not add a failure event solely to produce identical container delivery.
+
+Delimiter recovery scans valid alignment positions for a valid signature and CRC. It cannot use an unchecked length or the transmitter's private member list to bypass corruption. Record any broken delimiter chain even when a later member becomes recoverable. This distinction controls the reorder release rules.
+
+The decoder creates a separate Packet for each recoverable complete MPDU. Before it deletes the envelope, it projects the typed range outcome onto that member. It calls `setBitError(true)` on a failed member Packet. The [Rx FCS predicate](../../src/inet/linklayer/ieee80211/mac/Rx.cc) rejects that flag before declared-correct, declared-incorrect, and computed FCS branches.
+
+This uses the public Packet API without a false checksum or shared chunk mutation. Partial DATA failure must not mark the entire envelope incorrect or copy one global error flag onto every member. A failed delimiter still prevents member acceptance until valid bounded resynchronization.
+
+Rx validates each complete member through its current FCS predicate. A successful FCS check cannot override the member's PHY error flag. An invalid or unaddressed member cannot update receipt state, refresh inactivity, or trigger a response. Retain the applicable received NAV behavior for valid overheard members.
+
+The aggregate Rx entry must separate member checks from ordinary corrupt-frame callbacks. Rx records member rejection without an EIFS notification for each failed member. After all member checks, Rx reports one aggregate outcome for contention. If any member passes delimiter, PHY, and FCS checks, Clause 10.3.2.3.7 prohibits EIFS. This decision also counts valid overheard members and valid DATA without an agreement. With no correctly received member, Rx invokes the existing corruption procedure once for that PPDU.
+
+A partial member failure must not invoke `Hcf::corruptedFrameReceived()` or erase valid aggregate history. HCF receives the aggregate context with its member failures and delimiter facts. Common PHY failure still invalidates history through the current owner. In the main scenario, failed MPDU 101 causes neither EIFS nor loss of valid history for 100, 102, and 103. A separate all-member-failure case must exercise the existing corruption procedure without a Block Ack response.
+
+HCF processes valid members in receive order with explicit aggregate context. That context identifies PPDU membership, member position, delimiter continuity, and the valid member. The ordinary `NORMAL_ACK` path must not send individual ACKs for these members. The agreement handler records the valid DATA under the agreement even though its policy bits equal `00`.
+
+Validate the shared address, TID, policy, and Duration/ID conditions before agreement or delivery updates. A malformed context cannot produce success. For a valid addressed implicit aggregate under an active Basic agreement, report the profile conflict before receipt or reorder updates. Do not send a Basic response or silently reinterpret the implicit request. An absent agreement follows the separate recovery policy below.
+
+After the entire PPDU, HCF requests one immediate Compressed Block Ack when at least one valid addressed member qualifies. The response procedure uses the updated recipient scoreboard and its actual window start. Delivery does not erase receipt evidence. If all members fail, the recipient sends no Block Ack.
+
+For valid addressed Implicit BAR DATA without an agreement, the recipient discards the DATA. It sends no Block Ack or individual ACK. That DATA also triggers no DELBA, receipt state, reorder state, or upper delivery. Clause 11.5.4 explicitly names Block Ack-policy DATA in its absent-agreement DELBA rule. This first profile does not extend that rule to Implicit BAR DATA.
+
+Silence leaves the originator without immediate agreement-loss information. Finite batches already have a recovery trigger in [HCF](../../src/inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.cc). The lifetime timer expires DATA and invokes `completeTransfers()` when no exchange runs. Exchange completion also expires DATA and invokes that check. An accepted agreement with transmitted DATA retires when its peer/TID has no queued or numbered DATA.
+
+That trigger works with inactivity timeout zero. The new aggregate submission must retain each actual member's `recordTransmittedData()` update. This preserves the agreement's first-transmission condition and teardown boundary. HCF then uses its current originator-role DELBA procedure. Later DATA uses the current Normal Ack and ADDBA procedures.
+
+Continuous arrivals can prevent the empty-transfer condition. A failed implicit response therefore arms the explicit probe in [Section 4.7](#47-originator-exchange-timing-and-recovery). The recipient retains silent discard for implicit DATA. A later explicit BAR without an agreement retains S5's recipient-role DELBA response.
+
+In a hypothetical case, the recipient loses its agreement while fresh DATA continues to arrive at the originator. The recipient silently discards an implicit aggregate. The failed response wait arms one probe, which takes priority over further DATA for that agreement. The recipient receives the explicit BAR and sends DELBA. The originator retires its stale agreement and can establish a later agreement without queue drain.
+
+With an active agreement, valid Implicit BAR DATA refreshes recipient inactivity under Clause 11.5.4. A duplicate can refresh inactivity without a second delivery. Invalid members cannot refresh it. Preserve the current S5 behavior for an explicit BAR without an agreement.
+
+### 4.6 Reorder release and physical history
+
+Replace the single interframe boolean at the aggregate call boundary with the facts that the release decision needs. Reuse the S5 physical timing snapshots for real PPDU intervals. HCF retains previous aggregate delimiter facts and completed Block Ack history through its current history owner. The reorder service remains the owner of delivery decisions.
+
+Implement the applicable conditions in 10.25.6.6.2.1(a)(2)(i–iv), as well as the current individual-MPDU condition (v). The checks must distinguish a first member, a later member, and a standalone MPDU. A zero-length spacing delimiter does not become a DATA member. The receipt bit immediately before the scoreboard start and the required delimiter chain remain separate conditions.
+
+These are mandatory conditional actions within the supported HT-immediate profile. The clause states that the record `shall be modified as follows` before its release instructions. The [history table](#release-history-required-by-the-supported-profile) maps the fields to those conditions. Retain each history field only for a reachable release condition. A deferred condition needs evidence that the declared profile cannot reach it.
+
+Clear the relevant history after unrelated or abandoned receptions. Clear it after PHY acquisition or header failure. Receptions that overlap and incompatible local transmissions also invalidate the relevant history. A failed member alone does not erase valid PPDU history. Preserve its failure and delimiter facts for the applicable release check.
+
+A completed Block Ack can establish the history that the aggregate rules permit. No RIFS transmit scheduler enters this change. Reuse the current HT/BSS RIFS permission checks when a received sequence requires them.
+
+Tests must distinguish receipt from delivery. A positive Block Ack bit proves reception; it does not prove immediate upper delivery. No test may impose unconditional gap retention where an applicable aggregate release rule requires delivery.
+
+### 4.7 Originator exchange, timing, and recovery
+
+Add an aggregate transmit step followed by a Block Ack receive step. The exchange context identifies the peer, TID, transmitted member keys, and expected response kind. It reuses the current frame sequence timer. It does not construct an unsent BAR to obtain a timeout or response mode.
+
+The RXSTART deadline follows Clause 10.23.2.2: SIFS plus slot time plus the PHY receive-start delay, measured from PHY transmission completion. After an eligible reception starts, the step waits for its reception end and validates the response. Reuse the current HCF start/end reception mechanism. Do not replace it with an arbitrary whole-response delay.
+
+After actual aggregate submission, record every transmitted canonical member's allocation index through the current agreement owner. Queue commitment alone does not establish transmission. This preserves first-DATA tracking and the monotonic teardown boundary.
+
+All members use wire ACK policy bits `00`. The aggregate context determines that these bits mean Implicit BAR. The acknowledgment handler sets a Block Ack wait for each submitted member. Ordinary standalone MPDUs retain their current policy interpretation.
+
+Factor response-rate selection and response construction so explicit and implicit callers share the legal Compressed response rules. The implicit caller supplies the actual aggregate PPDU mode and valid response context. It must not pass a fabricated BAR or the mode of an unrelated member. Prediction and actual response selection must agree on legal format, rate, and airtime.
+
+The initial profile has no STBC or HT Control conditions that permit an HT/VHT control response. Block Ack therefore uses non-HT OFDM or ERP-OFDM under Clause 10.6.6.1. Choose the highest permitted BSS basic rate at or below the actual HT non-HT reference rate. If none qualifies, use the highest permitted mandatory rate at or below that same reference rate. Clause 10.6.6.5.2 applies this upper limit to both choices. Retain S5's explicit fallback for unknown BSS rate state.
+
+An alternate must preserve the primary response duration and modulation class under Clause 10.6.6.5.4. In a hypothetical HT MCS 0 case, no BSS basic rate qualifies. The available mandatory OFDM rates are 6, 12, and 24 Mbps. The non-HT reference rate is 6 Mbps, so the recipient selects 6 Mbps. A 24 Mbps fallback would exceed the reference rate.
+
+For aggregate protection, use legal non-HT RTS and CTS. A forced incompatible control format must prevent aggregate submission with a clear diagnostic. It must not create a new control-format violation. This restriction applies to the new profile; general control-response repair remains separate work.
+
+Clause 10.3.5 requires RTS/CTS when the complete PSDU exceeds `dot11RTSThreshold` in this HT profile. The complete length includes every member, delimiter, alignment interval, and zero-length spacing delimiter. A single member's length cannot supply that decision. Equality does not require protection under that clause, but the current RTS policy also selects protection at equality. Retain that permitted policy choice and ordinary-frame behavior.
+
+After mode-dependent spacing, preparation constructs an aggregate preview with the exact PSDU length. HCF passes that preview and a valid selected-member header to the current RTS policy. The preview supplies length; the separate header supplies frame kind and receiver address. The preview uses copied member content before finalization and carries no outer MAC header. It establishes no commitment or transmission attempt and never reaches the radio. Every batch reduction rebuilds that preview and reevaluates protection before complete-cost admission at the same retained DATA mode.
+
+In a hypothetical case, one retained retry and one new preview each contain a complete 1500-octet MPDU. Zero extra spacing gives a complete PSDU length of 3008 octets. With an RTS threshold of 2346 octets, HCF requires RTS/CTS although neither member exceeds that threshold. If CTS does not arrive, HCF records one contention failure. It discards the unused previews without commitment of the queued source. The retained member keeps its retry state and deadline.
+
+HCF checks the complete aggregate exchange before transmission: optional RTS/CTS, required interframe spaces, DATA PPDU, and the expected Block Ack. The retained mode and exact delimiter padding control DATA airtime. All members receive the same Duration/ID value, referenced to PPDU end. Recipient response transmission retains the current local NAV choice.
+
+A zero TXOP limit permits one A-MPDU DATA PPDU with its required protection and acknowledgment under Clause 10.23.2.9. It does not permit a second DATA PPDU. For a positive limit, every multi-MPDU aggregate must fit, even when all members are retries. The same-size retry exception can apply to a singleton aggregate only when no other DATA or management frame enters that TXOP. An initial singleton ordinary MSDU under the agreement can use its specified exception. A first-attempt A-MSDU cannot use that exception.
+
+Validate Block Ack addresses, profile, TID, length, fragment field, and bitmap before success. Reuse S5 modulo-4096 mapping and its rule that positive bits apply only to transmitted MPDUs. A partial bitmap releases only positively acknowledged canonical members. Absent or invalid responses end the batch wait without false success or inactivity refresh. They arm the probe before DATA retry.
+
+Response reception must satisfy the active frame sequence's receive interval and expected peer, TID, and profile. A response outside that interval cannot complete the wait. A callback from a retired local context cannot complete a later wait. The supported peer sends each immediate response at the required SIFS boundary. No additional exchange identifier exists in the supported Compressed Block Ack format.
+
+In a hypothetical injected case, an old probe response enters a later active interval with identical addresses, TID, SSN, and bitmap. Observable fields cannot distinguish that frame from a valid response to the later exchange. Apply normal response validation and transmitted-only bitmap updates; do not promise rejection based on its hidden origin. This profile adds no simulator-only correlation tag or replay detector.
+
+Use DATA retransmission for unacknowledged members, as permitted by 10.25.6.7.1. A singleton A-MPDU can carry the last retry member. An absent or invalid implicit response first causes one explicit Compressed BAR probe. A valid Block Ack with zero positive bits completes the response exchange and does not arm that probe.
+
+The required probe is an INET policy choice beyond the standard's requirements. Clause 10.25.6.7.1 also permits direct DATA retransmission after a lost Block Ack. This version retains one probe after the first failed implicit response to expose a stale agreement under continuous traffic. Each submitted probe adds BAR airtime, channel access delay, and a Block Ack response exchange or timeout. A healthy agreement incurs that cost too. Transfer completion can retire an empty agreement before probe submission.
+
+When the implicit wait ends without a valid matched Block Ack, HCF sets one pending-probe flag on the current originator agreement. It ends the sequence and returns to contention. The BAR policy prioritizes that peer/TID's probe before further DATA for the same agreement. Other eligible peers and TIDs can continue.
+
+Represent the pending state with one boolean in the current originator agreement. HCF derives its EDCAF from the agreement's TID through current EDCA classification. BAR selection compares that derived category with the candidate EDCAF. Only that EDCAF can submit or count the probe. The fixed TID mapping needs no retained category or second mapping table.
+
+The pending flag survives member expiry and admission refusal until submission or agreement retirement. This state is necessary because the next contention grant occurs after the failure. The flag belongs to the current agreement; add no separate recovery map, failure counter, or timer. Empty-transfer completion takes priority over an unsent probe.
+
+The probe uses the agreement's current transmit-window start and the actual explicit Compressed BAR exchange. It bypasses ordinary BAR threshold deferral. The acknowledgment handler attaches retained transmitted members within its bitmap range to that BAR wait. Selection must not depend only on `BLOCK_ACK_NOT_YET_REQUESTED`.
+
+Actual probe submission consumes the pending flag. A valid probe response applies the ordinary transmitted-only bitmap rules and permits eligible DATA retry. A failed probe returns to DATA retry through current control recovery if the agreement remains active. It does not arm another probe by itself. For each agreement, at most one submitted probe occurs between DATA attempts.
+
+A valid recipient-role DELBA uses the current lifecycle handler to retire the agreement. Reset and teardown clear an unsent probe with its agreement. An accepted probe retains its physical completion context under S5's retirement rules. [Appendix C](#appendix-c-recovery-transitions-and-acceptance-cases) defines the transitions and direct acceptance cases.
+
+Recovery counters and rate-control feedback describe different outcomes. A valid matched Block Ack completes one successful response exchange, even when its bitmap has no positive bits. This follows the response-based success definition in Clause 10.23.2.2. Unacknowledged members still need retry. An absent or invalid response produces one contention failure, not one failure per member. Preserve the current HCF choice to end the sequence and return to contention after response failure.
+
+Extend the recovery owner's API to separate per-member attempt counts from access-category contention updates. Do not call `dataFrameTransmissionFailed()` once per member: that method also changes station counters and CW. Use current member keys and count storage where their meanings fit. Keep Block Ack DATA retries subject to the current lifetime bound; add no new member retry limit.
+
+For an active agreement, aggregate RTS failure before DATA submission applies one ordinary control contention failure through the current recovery owner. This rule covers batches of new members, committed members, and both together. It adds no member retry increment, DATA attempt, DATA outcome, or pending probe. Queued sources remain queued; committed members retain their numbers, acknowledgment state, recovery state, and deadlines. Preparation releases its previews. Expiry and agreement retirement remain independent disposal causes.
+
+An aggregate RTS failure does not invoke the current per-frame RTS retry limit or discard a representative member. At the access-category retry boundary, the recovery owner applies the current contention reset without member disposal. This aggregate policy extends the current uncommitted-preview path to every aggregate composition. Clause 10.23.2.12.1 requires associated-RTS frame retry increments for DATA outside a Block Ack agreement; it does not require them here. Current individual-frame RTS recovery and internal collision recovery retain their existing counter and disposal rules. Current lifecycle resolution suppresses obsolete completion feedback.
+
+Record each attempted member's outcome through the current recovery and observation owners. Positive bitmap members report success; other attempted members report failure and retain eligibility until expiry or other current terminal disposal. Report terminal disposal once. The first aggregate transmit profile has no rate-control submodule, so these outcomes cause no `IRateControl::frameTransmitted()` calls. Current individual-frame configurations with rate control retain their existing callbacks. Adaptive aggregate feedback requires a separate design that preserves the actual PPDU mode across all member results.
+
+A probe adds no DATA attempt or duplicate per-member feedback. Its own contention outcome follows ordinary BAR accounting.
+
+### 4.8 Reuse and justified additions
+
+These choices apply [minimal design](../../doc/project/rule/quality.md#qr-design-minimal), [reuse](../../doc/project/rule/architecture.md#ar-ext-reuse), and [contract purity](../../doc/project/rule/architecture.md#ar-org-contract-purity).
+
+| Addition or extension | Required owner and consumers | Why current state is insufficient |
+| --- | --- | --- |
+| `HtMpduAggregationPolicy` and supplied selection limits. | QoS data service and replaceable policy. | The default policy deliberately selects no aggregate. An explicit HT policy preserves that default. |
+| Batch preparation and aggregate transmit step. | Data service, `InProgressFrames`, and frame sequence context. | One-MPDU preparation cannot retain independent members and one PPDU response wait. |
+| Aggregate start callback and cancellation result. | Tx calls HCF through the retained frame sequence context at the existing IFS boundary. | Current Tx copies an MPDU before SIFS and cannot revalidate an aggregate at its actual start. |
+| Shared MPDU finalizer and aggregate Tx entry. | Tx finalizes members before the builder constructs the envelope. | Ordinary Tx processing assumes one header and one FCS. It cannot finalize a delimiter envelope. |
+| Complete aggregate outcome for contention. | Rx supplies one outcome after member validation; HCF retains member history. | Ordinary rejection would invoke EIFS and clear history for an individual failed member. |
+| HT aggregation property and receive indication, owned by the PHY plan. | WLAN PHY supplies values to MAC receive entry and HCF. | Policy bits and member count cannot distinguish standalone DATA from a singleton aggregate. |
+| Cached aggregate reception, owned by the PHY plan. | WLAN receiver and current medium cache supply the result to MAC. | Whole-Packet corruption cannot report partial reception or delimiter continuity. |
+| Aggregate reorder context and history extension. | HCF and reorder service. | A single interframe boolean cannot represent the additional mandatory release conditions. |
+| Shared Compressed response source context. | Rate selection, recipient Block Ack procedure, and receive step. | Current public calls require an explicit BAR, which an implicit exchange does not transmit. |
+| Aggregate preview for RTS selection. | Preparation supplies exact PSDU length; the current RTS policy uses its separate member-header argument. | One member's length can omit required protection. The existing callable contract needs no outer aggregate MAC header or second threshold selector. |
+| One pending-probe flag in the current originator agreement. | HCF and the BAR policy retain a probe requirement across contention. | Continuous arrivals can prevent transfer completion. Member expiry must not erase the probe requirement while fresh DATA remains queued. |
+
+Keep new interfaces pure. Put reusable calculations in implementations. Add no parallel agreement store, acknowledgment map, expiry timer, or generic Packet extension. The new metadata and classes belong in the WLAN MAC or PHY packages that own their facts.
+
+## 5. Affected components and compatibility
+
+| Change surface | Declarations and consumers |
+| --- | --- |
+| Capability propagation | MIB C++/NED, HT capability structures, management conversion, association and simplified-management consumers, serializer checks. |
+| Transmit preparation | QoS data-service contract and implementation, MPDU policy C++/NED contract, new HT policy, aggregate builder, `InProgressFrames`, lifetime handoff. |
+| PHY dependency | The PHY plan owns HT header/tag MSG inputs, HT-SIG serialization and validation, receiver, radio, and the error-model contract/base. It also owns the MAC coverage handoff before the MIB update, per-part decisions, receive metadata, and shared delimiter parsing. |
+| MAC receive path | MAC, Rx contract/implementation, aggregate decoder, recipient QoS data service and agreement handler, reorder contract and implementation. |
+| Exchanges | HCF, frame sequence declarations/steps/context, acknowledgment handler, recovery procedure, RTS policy consumers, Compressed response policy/procedure, response-rate contracts, Tx contract/implementation and callbacks. The aggregate transmit profile requires fixed DATA rates. |
+| Representation and observations | Current WLAN serializers and dissector, member region tags, semantic outcome signals, protocol-test observation boundary. |
+| User documentation | `WHATSNEW`, user guide, migration guide, a focused example, and scoped Wi-Fi evidence. |
+
+The proposed transmit configuration is:
+
+```ini
+**.mac.hcf.isBlockAckSupported = true
+**.mac.hcf.blockAckVariant = "compressed"
+**.mac.hcf.rateControl.typename = ""
+**.mac.hcf.rateSelection.dataFrameBitrate = 6.5Mbps
+**.mac.hcf.rateSelection.dataFrameBandwidth = 20MHz
+**.mac.hcf.rateSelection.dataFrameNumSpatialStreams = 1
+**.mac.hcf.originatorMacDataService.mpduAggregationPolicy.typename = "HtMpduAggregationPolicy"
+```
+
+`HtMpduAggregationPolicy` does not exist yet. The fixed rate above selects HT MCS 0 with one spatial stream under the required HT20 long-GI configuration. These settings also require compatible modes, usable peer HT state, and accepted agreements. The example must configure those conditions explicitly. The initial policy adds no local count or byte cap. It derives those limits from the agreement, peer, and PHY.
+
+Custom data services, MPDU policies, Tx/Rx modules, response policies, and response-rate selectors can need interface updates. The migration guide must list each changed method and distinguish unsupported capability from an empty valid result. It must cover aggregate start, cancellation, member finalization, and the complete receive outcome. The DATA rate-control API remains unchanged; aggregate transmit opt-in requires its HCF submodule to be absent. Current `BasicMpduAggregationPolicy` configurations retain their rate-control behavior. The PHY plan owns error-model migration and preserves operation without an error model.
+
+Custom RTS policies retain the current callable signature but must accept an aggregate preview with a separate valid member header. The supplied Packet represents the complete PSDU for length checks. It has no outer MAC header. The migration guide must explain that distinction and the aggregate RTS recovery rule. Current individual-frame recovery remains the compatibility control.
+
+Use current owner-emitted simulation signals for actual transitions. Aggregate-level observations count one PPDU; member-level observations identify each MPDU. Document that boundary wherever a recorder or protocol assertion consumes it. No synthetic per-member PHY transmission event may imply multiple PPDUs.
+
+The current [seal registry](../../doc/project/audit/seal-list.md) seals `src/inet/common/packet/`. This design uses its current public API and proposes no edit there. Recheck seals before implementation. General MAC stop/start recovery remains a current limitation; ordinary agreement reset and object destruction must still dispose of the new transient state safely.
+
+## 6. Implementation steps and dependencies
+
+Each step becomes one coherent commit or a short series with its tests and migration text. This plan defines the MAC feature PR. The PHY plan defines separate receiver implementation and broader evidence deliveries. Contract changes and their required consumers remain buildable together.
+
+The PHY plan defines two gates. D1 proves cached aggregate reception with deterministic probabilities through the production receiver and includes the shared NIST/Yans implementation. D1 also preserves default HT initialization and compatible ordinary reception. D2 completes the broader NIST/Yans and PHY compatibility evidence. A2 accepts D1; A6 can close deterministic integration before D2. A7 requires both A6 and D2.
+
+| Step and purpose | Responsible components and proposed change | Expected result and verification | Dependency |
+| --- | --- | --- | --- |
+| A0: Establish the merged implementation base. | Check source claims and record dependency heads now. Before implementation, require merged #1343, #1344, and #1345. Pin their integrated base and compare contracts. | Confirm the commented call, default policy, owners, and resolved choices. Record the feature-off baseline before source changes. Revise changed contracts or owners. | Read-only checks can start now. Closure requires all three merges. |
+| A1: Preserve capabilities and construct legal aggregates. | Extend MIB/management spacing state. Consume PHY P1 representation, HT-SIG serialization and validation, and bounded parsing. Implement aggregate construction calculations for spacing, serialization, and region-tag rules. | Capability and representation checks prove directional limits, valid aggregate bytes, and singleton identity after byte conversion. The PHY plan owns the shared wire fixture and HT-SIG round trips. | A0; PHY P1. |
+| A2: Accept deterministic PHY reception. | MAC/Rx consumers adopt the PHY D1 indication and aggregation property. Accept the four per-part decisions, header rejection, structural refusal, actual failure paths, and receive metadata contract. | Valid long-GI and short-GI aggregates supply copied PHY outcomes without a second error draw. Failure containers stop before MAC header or trailer peeks. Incomplete advertised receive-mode support fails before the MIB update. An absent model follows its supported error-free case. MAC receipt and response acceptance belong to A4 and A5. | A1; PHY D1. |
+| A3: Prepare and retain independent members. | Add the HT policy, dynamic member selection, and batch preparation. Require fixed DATA rates and reject aggregate transmit opt-in with a rate-control submodule. Select one mode before spacing and batch reduction. Apply A1's construction calculations at that mode. Include numbered, unsent candidates through current acknowledgment state. Preserve single-frame internal collision recovery and current queue/lifetime handoff. | Unsupported rate-control combinations fail before DATA attempts. Refusal preserves source ownership and sequence state. Numbered, unsent members retain their assigned numbers and first-DATA Retry value. Commitment retains independent deadlines. | A1. |
+| A4: Receive aggregates and apply mandatory release. | Project range outcomes onto separate member Packet flags before Rx validation. Integrate one contention outcome, receipt updates, reorder context, and history. | Physical reception proves FCS rejection, correct EIFS behavior, delimiter continuity, mandatory release, and exactly-once delivery. Silent discard creates no receive state. | A1, A2. |
+| A5: Complete the implicit exchange and probe recovery. | Add aggregate steps, Tx start/cancellation, member finalization, Block Ack wait, response calculation, admission, and transmitted-member records. Select RTS from the complete aggregate preview after each reduction. Apply one contention outcome after aggregate RTS failure without member retry accounting. Derive the probe EDCAF from TID. Apply the observable response acceptance rules. | Exchanges prove computed FCS, SIFS cancellation, selective retry, response validation, probe priority, unchanged deadlines, and both HT duration conditions. Threshold cases prove PSDU-based protection. Mixed-batch RTS failure preserves member state at the contention retry boundary. A short-GI aggregate updates MAC receipt and receives one Compressed response after SIFS. At most one probe occurs between DATA attempts. | A2, A3, A4. |
+| A6: Verify negotiation, recovery, and compatibility. | Add infrastructure and compatibility coverage. Verify absent-agreement recovery for finite batches and continuous arrivals with zero and nonzero inactivity values. Verify DATA progress for a healthy agreement under controlled response loss. | Real management establishes limits. Tests prove transfer completion, probe-driven retirement without queue drain, later setup, continued DATA transmission, and historical compatibility. | A5. |
+| A7: Document and verify the supported profile. | Add the example, user and migration text, release note, and execution record. Reconcile relevant expected-failure declarations under separate authorization. | A6 and PHY D2 evidence support the documented profile. Required publication checks pass before any separately authorized publication. | A6; PHY D2. |
+
+A1 and A3 can supply reviewed preparation changes before the complete feature exists. A5 and A6 establish deterministic integration after A4. They do not establish NIST/Yans support. The final feature claim requires A7. Dependencies describe delivery order; they assign no concurrent agents.
+
+## 7. Verification methods and expected results
+
+### Proposed direct checks
+
+The seven fixtures below belong to this feature plan and remain proposed. The [PHY plan](ieee80211-ampdu-phy-reception.md#7-verification-methods-and-expected-results) owns three implemented fixtures: `Ieee80211AmpduWire_1.test`, `Ieee80211AmpduPhyReception_1.test`, and `Ieee80211AmpduPhyCompatibility_1.test`. P4 adds the third fixture for parent-compatible ordinary reception comparisons. The combined inventory is ten. The [PHY execution record](ieee80211-ampdu-phy-execution.md) states the completed PHY evidence and its limits. That evidence does not establish MAC feature completion.
+
+Fixed causal cases use seed 0. Injected agreements or deterministic error models establish only the stated boundary. Infrastructure cases separately prove production negotiation. [Appendix B](#appendix-b-verification-commands-and-campaigns) contains commands, campaign bounds, and evidence requirements.
+
+| Proposed fixture and category | Trigger and production boundary | Required observation |
+| --- | --- | --- |
+| `Ieee80211AmpduCapabilities_1.test`, module. | Full and simplified management with asymmetric receive limits. Include an absent aggregate role, a role without advertised short-GI support, and an absent error model. Use exact MAC inputs with a one-antenna limit and catalog modes outside that limit. | Exponents 0–3 and spacing values 0–7 reach the correct directional consumer; reset removes old peer limits. Absent roles fail before advertisement; incomplete coverage fails before the MIB changes local HT capability. Unsupported unadvertised modes do not cause rejection. An absent model retains valid HT initialization and negotiation. Compatible models retain advertised HT20 short-GI support. |
+| `Ieee80211AmpduPreparation_1.test`, module. | Production queues, HT policy, fixed DATA rates, batch preparation, and commitment. Include internal collision survivors and aggregate transmit opt-in with AARF. | Source identities, sequence wrap, independent deadlines, count/window/byte limits, and refusal before extraction. Numbered, unsent members keep their allocation and first-DATA Retry value. Internal collision terminal disposal still affects its selected frame only. Batch reduction retains one rate decision. AARF fails initialization with and without fixed-rate overrides. |
+| `Ieee80211AmpduReordering_1.test`, module. | Physical reception, member Packet flags, Rx FCS modes, and copied PPDU history. | Separate cases cover each reachable condition in 10.25.6.6.2.1(a)(2)(i–iv), retained condition (v), delimiter chains, and receipt evidence before the window. Declared-correct and computed FCS cannot accept a PHY-failed member. Partial loss preserves valid history and suppresses EIFS. Required release cannot become unconditional gap retention. |
+| `Ieee80211AmpduExchange_1.test`, module. | Production HCF, frame sequences, Tx/Rx, PHY, and recipient response. | Exactly one response after PPDU end and SIFS; no per-member ACK or successful-path BAR; singleton aggregate response remains Block Ack. Successful initial transmission and selective retry verify each member's final header and computed FCS. Without a qualifying basic rate, the recipient selects a mandatory fallback at or below the HT reference rate. |
+| `Ieee80211AmpduRecovery_1.test`, module. | Aggregate RTS failure, bitmap results, implicit response loss, probe refusal/results, expiry, DELBA, and reset. Cases cover new, committed, and mixed batches. Other cases cover absent or healthy agreements under finite or continuous traffic. | One contention outcome after RTS failure, unchanged member counters, no RTS-triggered member disposal, and no DATA outcome or probe. Other cases prove selective recovery, unchanged deadlines, safe disposal, derived probe ownership, and at most one probe between DATA attempts. No case permits duplicate DATA feedback. Finite and continuous traffic permit later setup. Healthy agreements retain DATA progress. |
+| `Ieee80211AmpduAdmission_1.test`, module. | Actual aggregate transmission under zero and positive TXOP limits, with and without RTS/CTS. Include threshold boundaries and batch reduction. | Complete-cost admission, separate L-SIG and 10 ms checks, one retained mode, and correct common Duration/ID. Complete PSDU length controls RTS selection after spacing and each reduction. Originator inactivity expiry during SIFS cancels DATA before new commitment or DATA attempt accounting. |
+| `N_AmpduImplicitBa.test`, protocol. | Production association and application traffic through AP and STA paths. | Negotiated limits, aggregate contents, implicit response, selective retry, and exactly-once upper delivery. |
+
+A2 accepts copied PHY indications from valid long-GI and short-GI aggregates through the real radio. Its checks stop at that indication and refusal boundary. A4 supplies member validation and MAC receipt. A5 owns the complete short-GI receive exchange in `Ieee80211AmpduExchange_1.test`, after A4. A2 requires neither MAC receipt updates nor a Block Ack response for closure.
+
+For A5, a fixture peer supplies legal aggregate bytes at an advertised HT20 short-GI mode through the real radio and MAC. Verify valid member receipt and one Compressed response after SIFS. Keep the local aggregate transmit policy at long GI. This case proves receive coverage; it does not establish short-GI selection from production transmit queues. A6 separately verifies the production capability negotiation.
+
+The PHY fixture separately supplies invalid encoded HT-SIG with successful header probabilities and without an error model. A2 verifies HEADER failure and refusal of any delivered container before MAC header or trailer peeks. Invalid CRC and unsupported field combinations use the current header-failure paths. A separate malformed-input case retains HEADER success but refuses insufficient modeled PSDU content at DATA. A4 and A5 verify no receipt update, delivery, or response after either refusal.
+
+The current [N_Ampdu](../../tests/protocol/wifi/11n/N_Ampdu.test) presence assertion needs the Compressed profile and supported HT configuration. Retain its aggregate observation and add completion evidence; do not replace it with a declaration. The current [Ac_Ampdu](../../tests/protocol/wifi/11ac/Ac_Ampdu.test) requirement remains deferred for actual VHT PPDUs. An inherited HT transmission from an `ac` interface cannot close that VHT requirement.
+
+Retain the current Compressed admission, exchange, input, interframe, and originator fixtures. Retain Block Ack scheduling, lifetime, lifecycle, association reset, fragment retirement, Basic, and disabled-feature checks. Map each changed contract to an explicit fixture before execution. A fixture with injected HCF responses does not prove PHY reception or association.
+
+The receive cases include partial loss, all-member failure, common header failure, and an overheard aggregate while local DATA remains queued. At least one correct member must suppress EIFS regardless of its address or agreement state. A delivered container with no correct member must invoke the existing corruption procedure once. Separate-part common header failure instead preserves the radio's abort and HCF history invalidation. Member rejection must not clear valid aggregate history through the ordinary HCF corruption callback.
+
+The computed-FCS exchange starts with valid production DATA and verifies a successful aggregate before it injects a selective member loss. Its retry changes the Retry bit and can change Duration/ID. Every transmitted member must pass the receiver's computed-FCS check after those edits. No outer FCS may replace a member FCS.
+
+Drive internal collisions through production EDCAF arbitration before aggregate preparation. Verify one numbered, unsent survivor without an aggregate commitment or DATA attempt. Later aggregate submission must preserve its number, deadline, and collision recovery state. Its first DATA transmission keeps Retry clear. A separate retry-limit case verifies current collision disposal before aggregate selection.
+
+For aggregate RTS failure, cover batches of new members, committed members, and both together. Include a committed candidate that remains `FRAME_NOT_YET_TRANSMITTED` and a transmitted retry. Withhold CTS through the access-category retry boundary. Verify one contention update per failed RTS and the current boundary reset. Verify unchanged member counters, numbers, acknowledgment states, and deadlines. No member disposal, DATA outcome, or pending probe may result from that failure alone.
+
+Absent-agreement recovery is an A6 acceptance gate. Cover finite batches and continuous arrivals with zero and nonzero negotiated inactivity values. For the finite case, force probe failure so DATA expiry reaches transfer completion. For continuous traffic, keep fresh DATA queued and provide a legal probe admission opportunity. Observe actual BAR, recipient DELBA, retirement, and later successful setup without queue drain.
+
+A valid zero-bit Block Ack must cause retry without a probe. Probe failure must permit a DATA attempt before another probe. Refusal must preserve the pending flag and DATA deadlines. Response tests cover frames outside an active receive interval, wrong peer/TID/profile, retired local callbacks, and an indistinguishable valid frame inside a later interval. The last case follows ordinary validation; no hidden exchange identity controls acceptance. [Appendix C](#appendix-c-recovery-transitions-and-acceptance-cases) maps the recovery cases to their expected results.
+
+A6 must also prove DATA progress for a healthy agreement under deterministic response loss. Keep fresh DATA queued with inactivity timeout zero. Appendix C separates full, partial, and failed probe cases and defines the required DATA observations. A5 checks the state transitions and counters for these cases.
+
+### Evidence status
+
+The source assessment checked the commented invocation, error-model APIs, mandatory release text, HT duration table, and prerequisite PR state. These are source and standards observations. No INET build, module test, protocol test, or simulation ran for this revision. Document checks cannot establish PHY or MAC behavior.
+
+All 144 local links resolve across the three documents. Changed prose passed sentence-length and paragraph checks. Hash checks confirmed preservation of the seven unrelated local files. These checks establish document properties only.
+
+The documents contain 12 Mermaid diagrams. One diagram label now states the aggregate RTS failure rule. No parser or browser check ran for this revision.
+
+## 8. Risks, unresolved decisions, and implementation scope
+
+The proposed transmit scope is HT20 with long GI. Reception also covers advertised HT20 short-GI modes. Other decisions include fixed DATA rates, explicit transmit opt-in, full-state receipt records, and cached partial PHY outcomes. Recovery uses DATA retry after one response-failure probe. These are model and implementation choices. They do not redefine IEEE ACK policy bits or the mandatory release conditions within the supported path.
+
+A threshold for consecutive failures remains optional follow-up work because it adds history and reset rules. Probe-cost measurements can guide a later trigger change.
+
+The source and standard checks resolve the main exchange choices:
+
+| Decision | Confirmed requirement or source behavior | Proposed implementation |
+| --- | --- | --- |
+| Implicit response format and rate. | 10.6.6.1 requires non-HT format for this profile. 10.6.6.5.2 explicitly covers Implicit BAR. | Factor the S5 selector around actual PPDU mode and addresses. Preserve primary/alternate rules. |
+| Absent agreement after aggregate reception. | 11.5.4 explicitly covers Block Ack-policy DATA. This profile does not extend that DELBA rule to implicit DATA. | Discard implicit DATA silently. Current completion handles a drained transfer; the new probe handles continuous arrivals. A6 proves later setup. |
+| Aggregate TXOP exceptions. | 10.23.2.9 excludes every multi-MPDU aggregate from the listed overrun exceptions. | Require full fit for multiple members. Check singleton composition and TXOP history before an exception. |
+| Response-failure probe. | 10.25.6.7.1 permits BAR or direct DATA retransmission after response loss. Current BAR selection omits failed implicit waits. Continuous arrivals can prevent transfer completion. | Require one probe as INET policy before further DATA. Consume it on submission; probe failure returns to DATA retry. A6 checks a healthy agreement under controlled response loss. |
+| Aggregate recovery counters. | 10.23.2.2 defines response success independently of bitmap delivery. Clause 10.23.2.12.1 separates ordinary associated-RTS retry increments from Block Ack operation. | Apply one contention outcome after aggregate RTS failure without member retry increments or disposal. Preserve current individual-frame and internal collision recovery. |
+| Aggregate RTS selection. | 10.3.5 applies the threshold to the complete PSDU. The current policy accepts a Packet and a separate MAC header. | Supply an exact aggregate preview and valid member header. Reevaluate protection after each reduction. Retain the current permitted equality policy. |
+| Aggregate rate control. | AARF can change rate and probe state after one member callback without knowledge of the actual PPDU mode. | Require fixed DATA rates and no HCF rate-control submodule for aggregate transmission. Leave adaptive aggregate feedback for a separate design. |
+| Response correlation. | Clauses 9.3.1.8.1 and 9.3.1.8.2 define no identifier for an individual exchange. | Validate the active receive interval and observable fields. Reject retired local callbacks. Apply ordinary rules to an indistinguishable valid frame. |
+| HT error-model compatibility. | HT length exponent zero advertises 8191 octets. Short-GI capability covers HT PPDU reception. MAC supplies effective limits to the MIB at link-layer initialization. BerTable already rejects HT operation strings. | Check role presence at PHY initialization. Validate exact MAC inputs before the MIB update. Preserve absent-model operation. Include shared NIST/Yans support for advertised HT20 receive modes in D1. Document BerTable's HT exclusion and compare non-HT parent controls. |
+| HT-SIG receive validation and content bounds. | Clause 19.3.21 distinguishes invalid HT-SIG CRC from physical carrier loss after a valid header. Current radio validation omits the CRC check. | P1 defines a shared stateless header validator. P2 applies it through current HEADER failure paths. A separate DATA bounds check refuses insufficient modeled content without a carrier-loss claim. |
+| HT mixed-format duration. | 10.27.4 limits calculated `L_LENGTH` to 4095. The separate 10 ms bound also applies. | Check both conditions through the HT mode contract before commitment and actual submission. |
+| Partial aggregate reception. | 10.3.2.3.7 prohibits EIFS when any member arrives correctly. Ordinary MAC rejection also clears HCF history. | Separate member rejection from one complete receive outcome. Preserve valid aggregate history. |
+| Deferred aggregate submission. | Current Tx copies and finalizes one MPDU before its IFS timer expires. | Use the existing timer boundary for HCF validation, commitment, member finalization, and actual aggregate submission. |
+
+Silent absent-agreement discard, one probe after a failed implicit wait, and the PHY independence assumption are model choices. The standard evidence does not establish measured error accuracy or performance. A materially different owner, PHY abstraction, or supported profile requires a plan revision before its implementation.
+
+The PHY prerequisite contains the partial reception risk. D1 must pass before the MAC exchange can claim deterministic selective recovery. It includes the NIST/Yans implementation and basic compatibility checks. D2 must pass before the full NIST/Yans profile forms part of the feature claim. A whole-PPDU failure plus a MAC-injected bitmap does not establish this feature. A second risk is incorrect aggregate history: a valid later delimiter must not repair a broken continuity condition silently.
+
+The current short-GI, VHT negotiation, control-response, channel-width, LDPC, and Minstrel topic branches are not assumed prerequisites. The new transmit profile retains its stated limits. The short-GI receive checks use current mode calculations; they establish no new short-GI transmit or duration-repair claim. Reconcile shared interfaces against their actual heads before any later integration. This plan does not authorize a merge or a rebase of those branches.
+
+The authorized deliverable is this document revision and the linked PHY plan. Implementation, expectation changes, protected-source edits, commits, and publication retain their separate task scope. The current design needs no protected Packet API edit. If that changes, update the design and obtain the permission that the seal owner requires.
+
+## Appendix A. Standard requirements and evidence
+
+IEEE Std 802.11-2024 is the normative reference for this profile. Its local source is `80211ax-2024.pdf`; the filename does not identify the document revision correctly. The corpus document ID is `ieee80211-2024`. Physical PDF pages below differ from printed page numbers.
+
+| Requirement | Normative source | Physical PDF pages and corpus locator |
+| --- | --- | --- |
+| ACK policy bits `00` mean Implicit BAR inside the applicable aggregate context. | Clause 9.2.4.5.4; Table 9-13. | 676–679; `ieee80211-2024:clause:9.2.4.5.4`, `ieee80211-2024:table:9-13`. |
+| The modeled HT-SIG header preserves PSDU length, Aggregation, field positions, and the specified CRC. | Clause 19.3.9.4.3; Table 19-11; Figure 19-6; Clause 19.3.9.4.4. | 3431–3434; `ieee80211-2024:clause:19.3.9.4.3`, `ieee80211-2024:table:19-11`, `ieee80211-2024:figure:19-6`, `ieee80211-2024:clause:19.3.9.4.4`. |
+| Invalid HT-SIG CRC prevents PHY-RXSTART. Unsupported field combinations also require rejection through the PHY receive procedure. | Clause 19.3.21. | 3486–3487; `ieee80211-2024:clause:19.3.21`; PDF fallback for the receive validation text. |
+| Signal loss after a valid header produces CarrierLost. A short modeled PSDU fixture does not establish that physical behavior. | Clause 19.3.21. | 3486–3487; `ieee80211-2024:clause:19.3.21`; PDF fallback for the carrier-loss distinction. |
+| Compressed Block Ack carries addresses, TID, sequence control, and bitmap, without an identifier for an individual exchange. | Clauses 9.3.1.8.1 and 9.3.1.8.2. | 722–723; `ieee80211-2024:clause:9.3.1.8.1`, `ieee80211-2024:clause:9.3.1.8.2`. |
+| Each member has a delimiter and its own MPDU/FCS. Nonfinal HT subframes align to four octets; the final subframe has no alignment padding. | Clause 9.7.1; Figures 9-1326 through 9-1331; Table 9-659. | 1840–1843; `ieee80211-2024:clause:9.7`, `ieee80211-2024:figure:9-1326`, `ieee80211-2024:table:9-659`. |
+| An HT delimiter uses the 12-bit low length field. Its CRC covers the previous 16 bits; the signature is `0x4E`. | Clause 9.7.1; Table 9-659. | 1841–1842; `ieee80211-2024:table:9-659`. |
+| Members share the applicable address, ACK policy, and Duration/ID conditions. | Clause 9.7.2; Clause 10.12.1. | 1843 and 1976; PDF fallback for the exact subclauses. |
+| The receiver advertises maximum length and minimum MPDU start spacing. Maximum HT length is `2^(13 + exponent) - 1` octets. | Table 9-225; Clauses 10.12.2 and 10.12.3. | 1126, 1976–1979; `ieee80211-2024:table:9-225`, `ieee80211-2024:clause:10.12.3`; PDF fallback for 10.12.2. |
+| Short-GI capability describes reception of HT PPDUs, without a separate restriction to standalone MPDUs. | Table 9-224. | 1124–1125; `ieee80211-2024:table:9-224`. |
+| Additional spacing uses zero-length delimiters. The selected PHY data rate controls the required spacing. | Clause 10.12.3, Equation 10-12. | 1977–1979; `ieee80211-2024:clause:10.12.3`. |
+| This HT profile requires RTS/CTS when the complete PSDU exceeds the RTS threshold. Additional protection remains permitted. | Clause 10.3.5. | 1930; `ieee80211-2024:clause:10.3.5`. |
+| Associated-RTS frame retry increments apply to DATA outside a Block Ack agreement. Internal collisions separately increment the affected frame retry counts. | Clause 10.23.2.12.1. | 2016–2017; `ieee80211-2024:clause:10.23.2.12.1`. |
+| HT PPDU duration must satisfy its PHY limit; TXOP conditions apply separately. | Clause 10.13; Table 19-25; Clause 10.23.2.9. | 1982–1983, 3493–3494, and 2012–2014; `ieee80211-2024:clause:10.13`, `ieee80211-2024:table:19-25`, `ieee80211-2024:clause:10.23.2.9`. |
+| HT mixed format also requires calculated `L_LENGTH` at or below 4095, with the signal extension excluded from that calculation. | Clause 10.27.4; Equation 10-18. | 2091–2092; `ieee80211-2024:clause:10.27.4`. |
+| One or more correctly received aggregate members prohibit EIFS. | Clause 10.3.2.3.7. | 1891–1892; `ieee80211-2024:clause:10.3.2.3.7`. |
+| This profile's Block Ack uses non-HT OFDM or ERP-OFDM. Both basic-rate selection and mandatory-rate fallback remain at or below the HT non-HT reference rate. An alternate preserves the required duration and modulation class. | Clauses 10.6.6.1, 10.6.6.5.2, and 10.6.6.5.4. | 1942–1947 and 1950; canonical clause IDs use `ieee80211-2024:clause:`. |
+| The recipient updates its scoreboard before one Block Ack response after SIFS. At least one valid member must exist. | Clauses 10.25.6.3, 10.25.6.5, and 10.25.6.7.1. | 2060–2064 and 2068–2070; canonical clause IDs use `ieee80211-2024:clause:`. |
+| Mandatory aggregate release depends on delimiter validity and previous aggregate or Block Ack history. | Clause 10.25.6.6.2.1(a)(2)(i–iv). | 2065–2066; `ieee80211-2024:clause:10.25.6.6.2.1`. |
+| A lost Block Ack permits DATA retransmission. Received bits update only the applicable transmitted MPDUs. | Clauses 10.25.6.7.1 and 10.25.6.8. | 2068–2072; canonical clause IDs use `ieee80211-2024:clause:`. |
+| EDCA distinguishes a received response from absent reception; contention state belongs to the exchange. | Clause 10.23.2.2. | 1999–2001; `ieee80211-2024:clause:10.23.2.2`. |
+| Valid Implicit BAR DATA refreshes recipient inactivity. The explicit absent-agreement DELBA rule names Block Ack-policy DATA. | Clause 11.5.4. | 2566; `ieee80211-2024:clause:11.5.4`. |
+
+The corpus was rebuilt with the tracked standards launcher. Lint reports 76 heading ambiguities and 425 unresolved references across the corpus. This is not a clean global corpus. Structural retrieval lacks separate nodes for 9.7.1, 10.12.1, and 10.12.2. PDF text supplied their relevant requirements. Visual inspection of Table 9-13 confirmed the two distinct meanings of bits `00`.
+
+The retrieved references from 10.25.6.5 resolve, but the extractor omits some inline clause references from its reference list. Direct retrieval separately confirmed 10.25.6.3, 10.25.6.4, and 10.25.6.7.1. Partial-state and DMG/TDD requirements do not enter this profile. No absent corpus edge establishes a protocol exception.
+
+The review assessment also inspected physical PDF page 2065 and Table 19-25 on physical PDF page 3493. The release instruction is mandatory when its conditions hold. The table specifies `aPPDUMaxTime = 10 ms`. The Clause 10.13 reference to Table 19-25 resolves. Its unresolved 10.12.2 corpus edge does not alter the directly checked HT duration requirement.
+
+The latest review retrieved normative Clauses 10.27.4 and 10.3.2.3.7 through their canonical nodes. Visual inspection of physical PDF page 2092 confirmed the ceiling operation in Equation 10-18 and the 4095-octet prohibition. Clause 10.27.4 references to 6.7.4, 6.7.5, and Table 19-25 resolve. The extracted 10.3.2.3.7 reference to 10.3.7 also resolves. Its EIFS prohibition required no PDF fallback. These retrieved clauses add requirements that the separate 10 ms table value does not replace.
+
+The response review retrieved normative Clauses 9.3.1.8.1 and 9.3.1.8.2, plus Table 9-225. Their corpus text establishes the frame fields and the minimum advertised HT aggregate length. The references from 9.3.1.8.2 to Figures 9-54 and 9-48, and Clause 10.25.6.5, resolve. Table 9-225's reference to 10.12.3 also resolves. These checks required no additional PDF inspection.
+
+The serialization correction retrieved normative Clauses 19.3.9.4.3 and 19.3.9.4.4, Table 19-11, and Figure 19-6. Their corpus locators and physical pages appear in the table above. The clause references to Table 19-11 and Figure 19-8 resolve. The extracted text establishes the fields and CRC without additional PDF inspection. P1 must verify the actual encoded bytes against those requirements.
+
+The receive-contract review retrieved normative Table 9-224 and Clause 19.3.21. Table 9-224 defines short GI as a receive capability. Its reference to 19.1.4 resolves; its reference to 10.11 remains unresolved. Clause 19.3.21 references Figures 19-25 through 19-27, which resolve. The extractor places later receive text under Figure 19-27. PDF text inspection of physical page 3486 confirmed the invalid-CRC rule without reliance on that extraction boundary.
+
+The RTS and recovery correction retrieved normative Clauses 10.3.5, 10.23.2.12.1, and 10.23.2.2 through their canonical nodes. Their physical PDF pages appear above. The extracted references to 26.2.1, 10.27, and 10.3.2.11 resolve. Clause 10.23.2.2's extracted references also resolve. No additional PDF inspection was necessary for these text requirements. The aggregate RTS rule and retained internal collision path remain distinct implementation decisions within that evidence.
+
+### Release history required by the supported profile
+
+The table maps Clause 10.25.6.6.2.1(a)(2) to the required history. Here, `WinStartR` is the receipt window start; `WinStartB` is the reorder window start. The prior receipt bit is the bit for `WinStartR - 1`. Release across a gap also requires `SN > WinStartB` and the clause's other profile conditions.
+
+| Condition | Required facts | Reachable in this profile? |
+| --- | --- | --- |
+| (i) | The received MPDU is a later aggregate member. The prior receipt bit is set. Delimiters from the MPDU at that sequence number to this member are valid. | Yes. A later member in the same aggregate supplies the proposed case. |
+| (ii) | The first aggregate member follows an aggregate after SIFS/RIFS, or a Block Ack transmission after SIFS. The prior receipt bit is set. All delimiters after the MPDU at that sequence number in the prior aggregate are valid. | Yes. A first aggregate member can follow the prior Block Ack after SIFS within a positive TXOP. |
+| (iii) | The received MPDU follows an aggregate after SIFS/RIFS, or a Block Ack transmission after SIFS. The prior receipt bit is set. All delimiters after the MPDU at that sequence number in the prior aggregate are valid. | Yes. Individual fallback can follow Block Ack after SIFS within a positive TXOP. |
+| (iv) | The first aggregate member follows an MPDU after SIFS/RIFS, or an ACK transmission after SIFS. The prior receipt bit is set. | Yes. An aggregate can follow individual Block Ack-policy DATA after SIFS. |
+| (v), retained from S5 | The individual MPDU follows an MPDU after SIFS/RIFS, or an ACK transmission after SIFS. The prior receipt bit is set. | Yes. The retained individual-MPDU path supplies a SIFS case. |
+
+These are proposed reachability cases, not executed evidence. A4 must verify each case through physical reception. No condition is deferred in this revision. Any later deferral must identify the excluded path and its source or fixture evidence.
+
+HCF supplies member position, valid delimiter history, prior event kind, and physical intervals. The reorder service owns the release decision. These facts explain the history extension; they do not require a second receipt map or timer.
+
+## Appendix B. Verification commands and campaigns
+
+The bounded timing campaign uses both 2.4 GHz and 5 GHz, HT MCS 0 and 7, long GI, and a legal fixed non-HT response. It covers no protection and RTS/CTS under both current protection mechanisms. Test one case immediately below, at, and above each applicable admission bound. Derive boundary values from the selected mode. Separate clock tolerance from protocol airtime; do not add the HT signal extension twice.
+
+Set the RTS threshold immediately below, equal to, and above the same complete PSDU length. Verify required protection above the threshold and the current policy's permitted protection at equality. Include two individually short members whose aggregate exceeds the threshold. Add nonzero spacing that crosses the threshold. Reduce a batch across that threshold and verify renewed protection selection, complete-cost admission, and one retained DATA mode.
+
+Include the HT mixed-format boundary at `TXTIME - SignalExtension = 5484 us` where the selected mode can reach it. Verify that HCF refuses a 6 ms 5 GHz candidate despite its duration below 10 ms. Test the separate 10 ms predicate directly because legal HT mixed-format transmission reaches the L-SIG bound first.
+
+Place the originator inactivity deadline after CTS reception but before the delayed aggregate's SIFS timer expires. Let the production inactivity handler retire the agreement. Verify that the start callback refuses the cancelled preparation. No DATA submission, source commitment, sequence assignment, member failure feedback, or response-failure probe may result from that cancelled request. Independently scheduled expiry and teardown retain their current disposal effects. A separate reset case verifies cancellation before context release and no callback after release.
+
+Use two peers and two TIDs in isolation controls. Exercise sequence numbers around 4095/0 and buffer sizes 1, 2, and 64. Cover aggregate completion before, at, and after DATA expiry where those orders are reachable. Use deterministic probabilities for the essential selective-loss cases. The PHY plan owns NIST/Yans seeds 0, 1, and 2. Its campaign checks invariants and reproducibility, not statistical goodput.
+
+The current checkout provides the module, protocol, and fingerprint runners. Their CLI and selection rules were inspected. The commands below are planned; new fixture selectors cannot run until those fixtures exist. Use the checkout's normal OMNeT++ environment. A zero-case selection is `NOT_RUN`.
+
+```sh
+make MODE=debug -j$(nproc)
+
+inet_run_module_tests -m debug \
+ -f 'Ieee80211Ampdu(Capabilities|Preparation|Reordering|Exchange|Recovery|Admission)_1\.test$'
+
+inet_run_module_tests -m debug \
+ -f 'Ieee80211(CompressedBlockAck.*|BlockAck.*|Edca.*Lifetime.*)_1\.test$'
+
+inet_run_protocol_tests -p inet -m debug -w '^tests/protocol/wifi$' \
+ -f '/(N_AmpduImplicitBa|N_Ampdu|N_BlockAck|Ac_BlockAck|Ac_BlockAckFallback)\.test$' \
+ --log-file /tmp/inet-ampdu-protocol.log
+
+( cd tests/fingerprint && ./fingerprinttest \
+ -m '(showcases/wireless/(blockack|aggregation|fragmentation|qos|txop)|examples/(wireless/qos|adhoc/qos))' )
+```
+
+Preview each new selector with the runner's `--dry-run` option. Reconcile selected files and executed configurations. Do not run protocol runners concurrently against shared generated directories. Preserve logs and generated artifacts before a later run can replace them.
+
+Run the full Wi-Fi protocol suite once after the focused cases pass. Separate observed passes, declared expected failures, unexpected failures, and setup errors. Expected-failure declarations for unrelated unsupported features remain visible. A changed marker or expected result follows [baseline rules](../../doc/project/rule/testing.md#tr-baseline-deliberate); source approval alone does not authorize that change.
+
+For each result, record HEAD, local source diff, environment, library mode, exact command, selected files, configuration, run, seed, verdict, and artifact path. Compare affected behavior with the pinned parent under identical inputs. Fingerprint differences require causal explanation and direct correctness evidence. Graphical `tyf` coverage needs its own environment and must not receive an inferred pass.
+
+Before publication, follow the [gate procedure](../../doc/project/guide/run-the-gates.md). It requires debug and release compilation, scoped correctness evidence, and the applicable architecture, naming, interface, seal, and series checks. Use the actual implementation base for range checks. No publication command forms part of this plan request.
+
+## Appendix C. Recovery transitions and acceptance cases
+
+The pending probe is one boolean in the originator agreement. It exists because response failure and the next admission occur at different events. Member expiry can occur between those events. Current EDCA classification derives the responsible access category from the agreement's TID. HCF coordinates the state with the current BAR policy and acknowledgment handler. It creates no retained category, second mapping table, separate agreement store, or periodic recovery task.
+
+| Trigger | Required state change and result |
+| --- | --- |
+| An implicit wait ends without a valid matched Block Ack. | Set the agreement's pending-probe flag. End the sequence with one contention failure. Defer further DATA for that peer/TID until the probe attempt or retirement. |
+| A valid matched implicit Block Ack has no positive bits. | Leave the probe flag clear. Complete one successful response exchange. Retain unresolved members for DATA retry. |
+| Probe admission refuses transmission. | Retain the pending flag. Preserve all DATA deadlines and sequence state. Other eligible traffic remains available. |
+| The radio accepts the explicit probe. | Consume the pending flag. Only the EDCAF derived from the agreement's TID submits and counts the attempt. Retain its physical context and mark applicable transmitted members for that actual BAR wait. Use the current agreement window start. |
+| A valid matched Block Ack answers the probe. | Apply transmitted-only bitmap updates. Release acknowledged canonical members. Permit unresolved or new DATA under normal eligibility. Do not repeat DATA feedback. |
+| The probe ends without a valid matched response and its agreement remains active. | Apply ordinary BAR control recovery. Return unresolved members to DATA retry. Do not re-arm a probe until another implicit DATA wait fails. |
+| A valid recipient-role DELBA arrives. | Use current lifecycle retirement and Action invalidation. Clear unsent probe state. Preserve any accepted physical completion context. |
+| A finite transfer becomes empty at a safe boundary. | Run current transfer completion before an unsent probe. Retire the accepted agreement and send originator-role DELBA independently of inactivity timeout. |
+| Reset or teardown retires the agreement. | Remove its pending flag with that agreement. Another peer/TID retains its state. |
+
+Continuous-traffic recovery assumes a responsive peer and a legal BAR admission opportunity. A6 must keep fresh DATA queued while the probe occurs. A BAR received without a recipient agreement triggers S5's DELBA path. The originator then retires its stale agreement before later setup.
+
+The finite-batch case must reach the current completion trigger even if probes fail. Preserve actual-member transmission records so the completion predicate can succeed. Withhold probe recovery responses until DATA expires, then permit normal teardown completion. This is a deterministic fixture condition, not a claim that radio loss always ends within a fixed time.
+
+Healthy-agreement recovery is a separate A6 case. Production management establishes accepted agreements at both peers. Fresh DATA remains queued with inactivity timeout zero. Deterministic PHY outcomes cause selected implicit Block Ack losses. The partial-bitmap case also causes selected DATA reception failures. Each case provides legal access for the probe and subsequent DATA.
+
+| Probe result under a healthy agreement | Required observation |
+| --- | --- |
+| A valid probe Block Ack acknowledges all retained transmitted members. | Release those members. Observe a new DATA transmission before another probe. |
+| A valid probe Block Ack acknowledges some retained transmitted members. | Release only those members. Retain unresolved members for retry. Observe the next eligible DATA transmission before another probe. |
+| The probe response is absent or invalid. | Observe an eligible DATA transmission before another probe while the agreement remains active. |
+
+In every case, the agreement remains active while fresh DATA remains queued. Each required DATA observation belongs to the tested agreement. Probes preserve DATA deadlines and add no DATA attempt or duplicate member feedback. Once normal responses resume, the recipient delivers each accepted MSDU once. The cases prove DATA progress under controlled access and loss conditions. They establish no general fairness or goodput bound.
+
+Expire the members from the failed DATA attempt before probe admission while fresh DATA remains queued. The probe must still transmit. This case proves that member expiry does not erase the agreement's retained probe requirement. A second access category must neither submit that probe nor receive its contention outcome.
+
+The probe waits for Block Ack. DELBA follows the current Action path and can arrive after that wait. It still retires the agreement. Current lifecycle resolution suppresses obsolete probe completion feedback.
+
+The probe is a submitted control exchange, not a synthetic input for implicit response calculations. It adds no DATA transmission attempt. Its positive bitmap cannot acknowledge an unsent or already discarded member. A result from a retired local context cannot resolve another exchange. Received frames follow the active receive interval and observable validation rules in [Section 4.7](#47-originator-exchange-timing-and-recovery). No probe result restarts a DATA lifetime.
diff --git a/plan/pending/ieee80211-ampdu-phy-execution.md b/plan/pending/ieee80211-ampdu-phy-execution.md
new file mode 100644
index 00000000000..7c4deb650a2
--- /dev/null
+++ b/plan/pending/ieee80211-ampdu-phy-execution.md
@@ -0,0 +1,154 @@
+# PHY P0 through P4 execution record
+
+The user authorizes P0, P1, and P2 on `4fa5cc0de15ee57388bcb3f20e866cab5972a670` before PRs #1343, #1344, and #1345 merge. This exception replaces the merge prerequisite for this execution only. The user also authorizes commits and a branch push. No baseline change forms part of this task.
+
+The active checkout is `/home/user/omnetpp_ws/inet-ieee80211-ampdu-implicit-blockack`, on branch `work/ieee80211-ampdu-implicit-blockack`. The implementation uses the approved base. The commit series separates the plans, P1 representation, and P2 reception.
+
+P0 through P4 are complete on 2026-10-17. The implementation supplies HT-SIG bytes, validation, bounded ranges, and cached partial-reception outcomes. P3 verifies NIST/Yans probabilities and seeded outcomes. P4 closes ordinary reception comparisons with the pinned parent. D1 and D2 are complete for the declared PHY profile. MAC acceptance and implicit Block Ack remain pending.
+
+## Implementation contract
+
+The radio owns HT-SIG fields and CRC construction. Its serializer preserves all 48 bits, including the received CRC. A shared validator rejects invalid CRC and unsupported fields before any range calculation. The existing mode and PSDU length remain the duration inputs.
+
+The shared delimiter inspector owns bounded range discovery. It checks delimiter bytes and advances at each scan. Delimiters and complete MPDUs have separate ranges. Alignment padding has no range outcome. MAC acceptance remains outside P1 and P2.
+
+The WLAN receiver owns PHY outcomes. The medium cache owns the PREAMBLE, HEADER, DATA, and WHOLE decisions. Direct success queries reuse those decisions. DATA retains copied range values; the result tag copies those values without reception pointers. A partial DATA failure preserves the aggregate bytes.
+
+The optional WLAN error-model contract supplies pure support queries and range probabilities. The shared WLAN base implements this contract for NIST and Yans. The MAC supplies its exact capability inputs before the MIB update. An absent model retains the current common PHY checks without error draws.
+
+The proposed wire fixture checks independent bytes, CRC, bounded ranges, and singleton identity. The proposed receiver fixture checks actual radio reception, range loss, query reuse, failure boundaries, metadata, and compatibility. Existing HT/VHT controls check ordinary reception. Debug compilation precedes behavioral checks. Scoped architecture checks cover both WLAN subtrees.
+
+The contract matches the inspected callers and owners. Source edits exclude the sealed `src/inet/common/packet/` subtree. Required input changes include MSG fields, serializer registration consumers, NED contracts, the MAC capability handoff, and migration text.
+
+## P0 source audit
+
+The cache key contains receiver, transmission, and signal part. `RadioMedium::getReceptionDecision()` inserts a decision after a cache miss. `CommunicationCacheBase::setCachedReceptionDecision()` deletes a replaced decision. `ReceptionCacheEntry` deletes all retained decisions at destruction. `receivePacket()` removes and deletes the result but retains the decisions. The result owns its packet and decision-vector container; it does not own the decisions.
+
+The direct `RadioMedium::isReceptionSuccessful()` call bypasses the decision lookup. The WLAN override must route aggregate queries through that lookup. PREAMBLE cannot calculate HEADER or DATA. Separate-part radio reception aborts after PREAMBLE or HEADER failure. Whole-signal result construction requests WHOLE.
+
+The HT header mode declares 48 bits. The transmitter derives duration from the selected mode and `lengthField`. The empty parent serializer does not alter these duration inputs.
+
+The metadata audit found three error-rate result filters. Each filter forwards present values without arithmetic. The radio attaches histogram recorders. OMNeT++ `StatisticsRecorder::collect()` ignores `NaN` for unweighted statistics. The receiver fixture must exercise these configured consumers before D1 closes.
+
+The direct reader in `L3NetworkConfiguratorBase` constructs an ordinary MTU-sized probe. It calls radio encapsulation without an aggregate request or delimiter content. Thus, it does not consume aggregate probability metadata. Ordinary metadata retains its current calculation. Other source matches either produce tags or concern unrelated error-model interfaces.
+
+BerTable's NED enum rejects the radio's `n(mixed-2.4Ghz)` string before initialization. An isolated BerTable with the allowed `n` value reaches its initializer, which also rejects HT. Both failures precede the new role check. Its packet-level PER calculation also dereferences the transmission bit model. The parent scalar control initializes with a valid table, then crashes at that null access. The feature library produces the same crash. This result establishes a prior limitation, not supported BerTable reception. The inherited receiver stage count covers the physical-layer check.
+
+## Evidence
+
+The existing HT/VHT module control exposed a required compatibility correction. `Ieee80211VhtSignalMode::createHeader()` returns an HT header in the parent. HT field construction therefore receives VHT's absent HT MCS value and refuses it. The VHT mode must create its existing VHT header type. This change preserves VHT duration and probability calculations and adds no VHT serializer implementation. The affected mode header is unsealed. The existing HT/VHT control verifies this required extension of the change surface.
+
+The wire fixture verifies golden bytes from IEEE Std 802.11-2024, Figure 19-6 and Clause 19.3.9.4.4. A polynomial long-division calculation supplied the fixed CRC constants independently of the production shift register. Fresh byte-backed packets establish decode without the original typed header. Cases cover standalone and singleton identity, short GI, invalid CRC, unsupported fields, incomplete headers, and improper representation. HT Length 65535 and MCS 31 survive conversion; length 65536 causes refusal.
+
+Delimiter cases cover lengths 0 and 4095, refusal of 4096, all alignment remainders, CRC and signature errors, truncation, final padding, and bounded recovery. An overrun delimiter cannot supply a valid member boundary.
+
+The receiver fixture uses the real radio and medium with scalar and dimensional reception. It executes 31 scenarios in both reception modes, for 124 seed-0 runs. The scenarios cover ordinary NIST/Yans reception, an absent model, partial loss, delimiter and spacing-delimiter failure, total member failure, and failed common parts. Invalid CRC, unsupported fields, and structural refusal use distinct paths. Short-GI aggregate reception preserves the copied indication. An absent-model case with an unmet SNIR threshold supplies no accepted header or ranges and consumes no draws.
+
+Repeated PREAMBLE, HEADER, DATA, WHOLE, direct success, and result queries add no draws or probability calls. The fixed-probability case consumes seven draws for seven ranges. Endpoint probabilities consume no draws. The fixture checks PREAMBLE before any HEADER or range call. It retains a copied indication after normal cache cleanup. The result preserves power, SNIR, time, mode, channel, and transmission identity.
+
+The capability cases create a production WirelessHost. A missing role fails at the physical-layer check. Incomplete short-GI coverage fails before the MAC updates the MIB. Complete one-stream coverage passes despite unsupported catalog modes above the antenna limit. An absent model passes, including the unit-disk configuration. A non-HT model without the role retains ordinary reception; its runtime change to an HT set fails.
+
+Separate cases verify BerTable's NED refusal, initializer refusal, and non-HT initialization.
+
+The configured radio filters and histogram recorders receive aggregate metadata. The recorded aggregate probability histograms have zero samples because their input values are `NaN`. Ordinary NIST/Yans controls retain a zero PER and unknown BER/SER. Finite-SNIR controls without a model retain no error-rate tag. This evidence closes the metadata consumer audit for the configured paths.
+
+The isolated parent checkout is `/tmp/inet-ampdu-parent-4fa5cc0de1`. Its six ordinary HT runs pass with NIST, Yans, and no model, in both reception modes. The feature's corresponding scalar controls retain 170 microseconds for a 100-byte PSDU at HT MCS 0 with long GI. NIST/Yans retain PER zero and BER/SER `NaN`. Every corresponding control retains the next RNG value `2357136044`.
+
+## Commands and artifacts
+
+Commands run from the active repository root unless this record states another directory. Debug is the behavioral test mode. Every new fixture uses seed 0. Existing fixtures retain their declared runs and seeds. Source changes require a fresh matching library before dependent tests.
+
+Fixture commands use `MAKEFLAGS=--jobserver-style=pipe` and `MPLCONFIGDIR=/tmp/ampdu-matplotlib`. `/tmp/ampdu-source-sha256.txt` identifies the 22 changed source and test files. Its SHA256 is `6ab4156726c29065523a250443ade2cc67cbedceb220c70627001d7ca1017e16`. The manifest includes the final wire fixture and receiver fixture. Documentation does not form part of that source manifest.
+
+| Check | Command or selection | Status and artifact |
+| --- | --- | --- |
+| Debug build | `make MODE=debug -j8` with `MAKEFLAGS=--jobserver-style=pipe`. | Pass, exit 0; `/tmp/ampdu-debug.log`. |
+| Release build | `make MODE=release -j6`, followed by `make MODE=release -j8` with the same `MAKEFLAGS`. | Both pass, exit 0; `/tmp/ampdu-release.log` and `/tmp/ampdu-release-final.log`. |
+| Wire fixture | `inet_run_unit_tests -m debug -f 'Ieee80211AmpduWire_1\.test$' --no-concurrent`. | Pass, exit 0; `/tmp/ampdu-wire.log`. |
+| Receiver fixture | `inet_run_module_tests -m debug -f 'Ieee80211AmpduPhyReception_1\.test$' --no-concurrent`. | All 124 runs pass, exit 0; `/tmp/ampdu-phy.log`. |
+| Unit controls | `inet_run_unit_tests -m debug -f 'Ieee80211(AmpduWire\|MpduSubframeHeaderSerializer\|MpduDelimiterWire\|HtModeSet\|HtModeCache\|VhtHtCapabilities)_1\.test$' --no-concurrent`. | Six fixtures pass; `/tmp/ampdu-final-unit.log`. The wire fixture has a later focused refresh. |
+| Module controls | `inet_run_module_tests -m debug -f 'Ieee80211(AmpduPhyReception\|VhtHtSupport\|HtAssociation\|HtAntennaRateControl)_1\.test$' --no-concurrent`. | Four fixtures pass, exit 0; `/tmp/ampdu-final-module.log`. That run includes 120 receiver scenarios. The later focused receiver run adds four absent-model SNIR controls. |
+| Functional fingerprints | From `tests/fingerprint`: `./fingerprinttest -d -F tyf -t 3 -m 'showcases/wireless/(aggregation\|qos)/' showcases.csv`. | All five configurations pass, exit 0; `/tmp/ampdu-fingerprints.log`. Graphical ingredients are excluded. No expectation changes occur. |
+| Architecture | `doc/project/enforcement/check-architecture.sh` for each WLAN subtree. | Both pass; `/tmp/ampdu-phy-architecture.log` and `/tmp/ampdu-mac-architecture.log`. |
+| New interface | `doc/project/enforcement/check-interfaces.sh src/inet/physicallayer/wireless/ieee80211/contract`. | One interface passes; `/tmp/ampdu-interfaces.log`. |
+| Source protection | `doc/project/enforcement/check-source-seals.sh --diff`. | Pass; `/tmp/ampdu-seals.log`. No sealed source path changes. |
+| Naming | Scoped naming checks in the active and parent checkouts. | The same three PHY and 32 MAC candidates remain. The PHY log differs only in one line number. `/tmp/ampdu-phy-naming.log`, `/tmp/ampdu-mac-naming.log`, and their `ampdu-parent-*` controls retain the comparison. |
+
+The parent build command is `make MODE=debug -j12` from the isolated checkout. Its runner uses the same receiver-fixture selector and debug mode after `source setenv -q`. `/tmp/ampdu-clean-parent-debug.log` and `/tmp/ampdu-parent-controls.log` retain its results. The parent fixture contains only the ordinary controls and the same node setup.
+
+The isolated BerTable reproduction is `tests/module/Ieee80211AmpduBerTableControl_1.test` in the parent checkout. The parent runner records the failure in `/tmp/ampdu-parent-bertable.log`. The same generated executable also runs with the feature library first in `LD_LIBRARY_PATH`. `/tmp/ampdu-feature-bertable-libraries.log` confirms that library path. `/tmp/ampdu-feature-bertable.log` records the same null-bit-model failure. The fixture supplies all eight table rates. These failures do not count as passing reception controls.
+
+The runner's dry-run path fails with `ValueError('list.index(x): x not in list')`. Those previews establish no behavioral result. Normal filtered runs select and execute the named fixtures. A FIFO collision also affected an early parallel test build. Later commands use `MAKEFLAGS=--jobserver-style=pipe`. Fixture setup corrections precede the recorded passing runs.
+
+## Standards evidence and limits
+
+The local corpus identifies `ieee80211-2024` as IEEE Std 802.11-2024. Table 19-11 spans physical PDF pages 3431–3432. Clause 19.3.9.4.4 is on page 3434. Its Figure 19-8 reference resolves. The corpus cannot resolve Clause 9.7.1, so PDF text on pages 1841–1843 supplies delimiter evidence. PDF text on pages 3486–3487 confirms invalid-CRC and unsupported-mode behavior. These clauses are normative. The corpus linter reports unrelated heading ambiguities and unresolved references; the selected CRC node is unambiguous. Local artifacts `/tmp/ampdu-htsig-clause.json`, `/tmp/ampdu-htsig-table.json`, `/tmp/ampdu-htsig-refs.json`, and `/tmp/ampdu-standards-lint.json` retain the retrieved evidence.
+
+The implementation does not enable MAC receipt, MPDU release, or an implicit Block Ack response. Feature A2 and A4 retain those acceptance duties. A failed MPDU range must become that member's bit-error flag before the normal Rx FCS check. The aggregate probability fields are not member estimates. P3 and P4 supply the additional evidence below. The commit and push authorization does not permit baseline changes.
+
+Independent range draws at the current scalar SNIR are a packet-level model choice. This abstraction does not model correlated FEC errors or interference changes within one member. Alignment padding contributes duration but has no separate outcome. These checks establish representation, integration, and selected regression behavior. They establish no new PHY accuracy result. Release-mode behavioral tests and feature-isolation builds remain unverified.
+
+The final self-audit traces cache ownership, prerequisite order, result copies, header refusal, structural refusal, capability publication, and the ordinary MPDU branches. It finds no unresolved defect in the implemented scope. The audit uses the current architecture, naming, quality, test, and WLAN guidance. It is a self-audit, not an independent review.
+
+## Publication checks
+
+The first publication attempt rejected Git metadata writes with `Read-only file system`. A separate repository at `/tmp/inet-ampdu-publication` held the commit series. Its source and test files matched the tested checkout. The separate `AGENTS.md` edit remained outside the series. Shell access could not resolve GitHub, so that attempt required the GitHub connector.
+
+The final debug and release builds pass in `/tmp/ampdu-push-debug.log` and `/tmp/ampdu-push-release.log`. The repeated filters pass six unit fixtures and four module fixtures. Their logs are `/tmp/ampdu-push-unit.log` and `/tmp/ampdu-push-module.log`. The new series passes commit, classification, include, and NED parameter checks. The audit supplies no PR description because this task creates no PR. Global architecture, naming, and interface findings match the pinned parent. Source seals pass. These checks establish final-tree behavior; intermediate commits have no separate compilation result.
+
+The repeated fingerprint filter passes all five controls without expectation changes. Its log is `/tmp/ampdu-push-fingerprints.log`. The full series since `origin/master` also passes its mechanical checks, with notes. The first publication attempt did not complete. The connector rejected the first tree request because tool approval was required and the approval policy was `never`. No remote branch update occurred. The original checkout retained its previous HEAD and local files.
+
+The user requested a retry after the permissions changed. Git metadata writes and shell access to GitHub now succeed. All 28 prepared files match the active checkout before commit transfer. The active branch now contains the three prepared commits. The separate `AGENTS.md` edit remains outside those commits. Only this publication record changes after the transfer; the tested source and test files remain unchanged.
+
+## P3 and P4 execution
+
+The user requested P3 and P4 after publication of P1 and P2. The active HEAD is `0221daf515e1bdc7dcaa79b12b6547c11a977d75`. The comparison parent remains `4fa5cc0de15ee57388bcb3f20e866cab5972a670`. The active checkout and branch remain those recorded above. This execution changes tests, migration text, and plan records. It changes no production source, interface, ownership rule, model assumption, or stored fingerprint expectation. The separate `AGENTS.md` edit remains unchanged.
+
+The additional module fixture separates ordinary reception from the new aggregate interfaces. The same fixture source compiles on both revisions. This necessary test separation adds one PHY fixture, so the combined PHY and MAC inventory increases from nine to ten. The MAC feature's seven fixtures remain proposed.
+
+### P3: NIST/Yans probabilities and seeded outcomes
+
+The extended `Ieee80211AmpduPhyReception_1.test` executes 468 runs. Its original 31 scenarios now run with seeds 0, 1, and 2. Eight additional scenarios cover NIST and Yans, MCS 0 and 7, and long GI and short GI. Every scenario uses scalar and dimensional reception with whole and separate signal parts. The bounded campaign adds no distributional or error-accuracy claim.
+
+Test subclasses call the unchanged NIST and Yans implementations through the production receiver. Each range probability must equal the existing per-length DATA calculation at the configured scalar SNIR. The subclasses observe calls; they do not substitute probabilities. A separate reference matrix checks all 64 HT20 modes, including MCS 0 through 31 with both guard intervals. Each mode uses seven SNIR inputs and lengths 4, 100, 101, 102, 4095, and 65535 bytes. The minimum and mean reductions use distinct synthetic SNIR values. The four matrix instances execute 10752 exact comparisons without random draws. This matrix establishes calculation coverage; actual aggregate radio runs use MCS 0 and 7.
+
+For example, a three-member aggregate includes a spacing delimiter and three alignment bytes. Seven scored ranges use the real model probabilities. An independent generator with the same seed replays the HEADER draw and each eligible range draw. The fixture compares every retained outcome, draw count, and subsequent random value with that reference. Partial member failure preserves the envelope and copied outcomes. Alignment padding causes no eighth range or extra draw.
+
+At the PREAMBLE boundary, repeated cached and direct success queries cause no HEADER or DATA call or draw. At the result boundary, repeated queries reuse all retained outcomes and metadata. The fixture also checks indication values after cache cleanup. All 96 real-model records match between whole and separate signal parts. A repeated execution produces the same 96 records. The campaign contains 76 all-good records and 20 partial-failure records. Both models produce partial failure in this campaign.
+
+### P4: Ordinary reception and compatibility limits
+
+`Ieee80211AmpduPhyCompatibility_1.test` executes 576 runs on each revision. The inputs cover NIST, Yans, an absent model, and a non-HT custom model without the aggregate role. The HT controls select MCS 0 or 7 with either guard interval. The custom model selects ERP OFDM at 6 Mbit/s. Each input uses scalar or dimensional reception, whole or separate signal parts, and seeds 0, 1, and 2. The non-HT controls repeat the unused MCS and GI labels but always select that same ERP mode.
+
+Three noise levels distinguish all-good controls, fractional probabilities, and failures. The low-noise power is -110 dBm. The two higher powers are -30 and -28 dBm for MCS 0, or -50 and -43 dBm for MCS 7. The receiver's energy threshold exceeds the configured noise power. This condition avoids an initialization failure that would supply no reception evidence. The SNIR threshold remains -10 dB for these bounded controls.
+
+All 576 records match the parent exactly. The comparison includes delivery count, packet error, duration, PER, BER, SER, received power, minimum SNIR, reception endpoints, draw count, and subsequent random value. Assertions also check mode, channel presence, and transmission identity availability. The records include 498 successful receptions, 66 delivered failure containers, and 12 attempts without a delivered container. NIST supplies the twelve separate-part header aborts. The absent and non-HT custom models add no error draws.
+
+The ordinary record stream has SHA-256 `f62dbea0e379e33e13df3246a2213d53ba6e52cc4a46852510f7f61bd5a01869`. `/tmp/ampdu-p4-comparison.json` records the fixture hash, revisions, counts, and equality result. The parent executable resolves `libINET_dbg.so` and `libtest_dbg.so` from the parent checkout. The active executable resolves the corresponding active libraries.
+
+The extended reception fixture retains the unsupported-model checks from P2. The production MAC handoff refuses a missing role and incomplete short-GI coverage before its MIB update. It accepts unsupported modes outside the effective NSS limit. The fixture retains absent-model, runtime mode-set, non-HT custom, and BerTable initialization controls. The migration guide now states independent range draws, scalar SNIR reduction, guard intervals, padding treatment, and accuracy limits.
+
+The separate BerTable reproduction still exits with SIGSEGV, status 139, on both revisions. Both logs identify `bitModel->getDataLength()` at line 62 with a null bit model. They are `/tmp/ampdu-p4-bertable-parent.log` and `/tmp/ampdu-p4-bertable-feature.log`. This comparison confirms the documented prior limitation. It is not a passing reception result. P4 does not repair BerTable or claim support for that configuration.
+
+### Commands and evidence
+
+All active commands run from `/home/user/omnetpp_ws/inet-ieee80211-ampdu-implicit-blockack` unless a command names another directory. Debug compilation precedes the tests. Release compilation also passes. The final source tree equals the published P2 source tree. The debug library has SHA-256 `3df8db6dba916c23bada8ea83583b41e9a425c81fc5a3bf90c83f7b75566044d`. The parent debug library has SHA-256 `877fa150f8cc41a21c765277aeb0ec478f99a6a9092a60462110496a68c99841`.
+
+```sh
+MAKEFLAGS=--jobserver-style=pipe make MODE=debug -j8
+MAKEFLAGS=--jobserver-style=pipe make MODE=release -j8
+MAKEFLAGS=--jobserver-style=pipe MPLCONFIGDIR=/tmp/ampdu-matplotlib inet_run_unit_tests -m debug -f 'Ieee80211(AmpduWire|MpduSubframeHeaderSerializer|MpduDelimiterWire|HtModeSet|HtModeCache|VhtHtCapabilities)_1\.test$' --no-concurrent
+MAKEFLAGS=--jobserver-style=pipe MPLCONFIGDIR=/tmp/ampdu-matplotlib inet_run_module_tests -m debug -f 'Ieee80211(AmpduPhyReception|AmpduPhyCompatibility|VhtHtSupport|HtAssociation|HtAntennaRateControl)_1\.test$' --no-concurrent
+```
+
+The build logs are `/tmp/ampdu-p34-debug.log` and `/tmp/ampdu-p34-release.log`. Six unit fixtures and five module fixtures pass. The unit log is `/tmp/ampdu-p34-units.log`. The final module log is `/tmp/ampdu-p34-final-modules.log`. The direct compatibility logs are `/tmp/ampdu-p4-feature.log` and `/tmp/ampdu-p4-parent.log`.
+
+The parent command uses the identical compatibility fixture at `/tmp/inet-ampdu-parent-4fa5cc0de1`, after `source setenv -q`, with the explicit filter `Ieee80211AmpduPhyCompatibility_1\.test$`. Its debug build remains the clean P0 parent build. The receiver's first model records are `/tmp/ampdu-p3-first-model-results.txt`. Later records match them exactly.
+
+The module fixture writes its detailed output under `tests/module/work//test.out` in each checkout. To reproduce the comparison, select the lines that start with `CONTROL ` from each compatibility output. Require exactly 576 lines in each file. Compare the lines byte for byte in their original order. The fixture's post-run checker separately requires all 576 successful simulation runs and unique input combinations. The reception fixture's checker requires all 468 successful simulation runs and equality across signal-part modes.
+
+Scoped architecture checks pass for both WLAN subtrees. Their logs are `/tmp/ampdu-p34-architecture-phy.log` and `/tmp/ampdu-p34-architecture-mac.log`. The source-seal check reports no changed production files. `git diff --check` passes. The earlier filtered fingerprint results remain evidence for the unchanged production source. This test-only extension requires no new fingerprint baseline or broad fingerprint campaign.
+
+Early fixture revisions failed on invalid noise thresholds, typename expression syntax, and an incorrect ERP header-length assumption. A post-run check also exposed insufficient ordinary failure coverage. The final fixture corrects those setup and coverage defects. The earlier mixed batches retain their actual failure status. They do not replace the final clean runs.
+
+The self-audit checks actual model dispatch, byte-to-bit conversion, mode coverage, SNIR reduction, probability bounds, cache order, sample reuse, metadata, and normal cleanup. It also checks identical parent inputs and library selection. It finds no unresolved defect in the requested scope. This result is a self-audit. Release behavioral tests, feature-isolation builds, correlated FEC behavior, and MAC acceptance remain outside this evidence. P4 closes D2 for the declared PHY profile, not the complete MAC feature.
diff --git a/plan/pending/ieee80211-ampdu-phy-reception.md b/plan/pending/ieee80211-ampdu-phy-reception.md
new file mode 100644
index 00000000000..5851cf79ce9
--- /dev/null
+++ b/plan/pending/ieee80211-ampdu-phy-reception.md
@@ -0,0 +1,364 @@
+# Implementation plan: partial PHY reception for HT A-MPDU
+
+> **Kind:** procedure · **Status:** draft · **Seal:** none · **Owns:** — · **Stands on:** [S5 execution record](ieee80211-compressed-blockack-s5-execution.md), [implementation plan procedure](../../doc/project/guide/write-an-implementation-plan.md), [IEEE 802.11 rules](../../doc/project/domain/ieee80211.md)
+
+Checkout: `/home/user/omnetpp_ws/inet-compressed-blockack-s5`, branch `work/compressed-blockack-s5`, HEAD `4fa5cc0de15ee57388bcb3f20e866cab5972a670`.
+
+Plan revision: 11, 2026-10-17. The user's `proceed, including the optional simplification` authorizes the reviewed document corrections. This document accompanies feature revision 12 and its revised summary. The feature plan defines PSDU-based RTS selection, recovery before DATA submission, and a boolean probe requirement with a derived access category. This PHY revision synchronizes those references; its behavior and delivery gates remain unchanged. Implementation, expectation changes, commits, and publication remain outside this authorization.
+
+The project plan procedure remains a draft.
+
+Execution status, 2026-10-17: P0 through P4 are complete under the user's approved exception to the merge prerequisite. D1 and D2 are complete for the declared PHY profile. The [execution record](ieee80211-ampdu-phy-execution.md) identifies the active checkout, checks, and limits. P4 adds one parent-compatible module fixture for exact ordinary reception comparisons. This test addition changes no production contract. The proposal below retains its original design context. MAC acceptance and the feature stages remain pending.
+
+## 1. Problem and intended behavior
+
+The WLAN receiver currently produces a whole-data success result. That result cannot describe a failed MPDU beside valid MPDUs in the same A-MPDU. The [MAC feature plan](ieee80211-ampdu-implicit-blockack.md) needs those separate results before it can acknowledge and retry members correctly.
+
+This hypothetical case defines the required result. One HT PPDU carries MPDUs 100 through 103. PHY acquisition and header reception succeed, but MPDU 101 fails. The receiver must preserve valid outcomes for 100, 102, and 103. Two later reads of that reception result must return the same outcomes without additional random draws.
+
+The PHY reports reception facts. The MAC separately checks delimiters, FCS values, addresses, and agreement state before acceptance. This plan supplies no Block Ack response or upper delivery by itself.
+
+```mermaid
+flowchart TD
+ RX["One received HT PPDU"] --> H{"Do PHY acquisition and
header reception succeed?"}
+ H -->|No| FAIL["Report no valid aggregate content"]
+ H -->|Yes| R["Obtain bounded delimiter
and MPDU ranges"]
+ R --> MODEL{"Is an error model present?"}
+ MODEL -->|Yes| E["Obtain each range probability
from the selected error model"]
+ MODEL -->|No| GOOD["Record physical success
Retain delimiter and FCS checks"]
+ E --> C["Sample when necessary
Retain immutable cached outcomes"]
+ GOOD --> C
+ C --> I["Radio supplies a typed indication
with copied range outcomes"]
+ I --> MAC["MAC applies delimiter, FCS,
address, and agreement checks"]
+```
+
+This diagram describes proposed behavior. It is not an executed trace.
+
+## 2. Current implementation and evidence
+
+The source assessment established the facts below at the pinned revision. The companion plan's [standards appendix](ieee80211-ampdu-implicit-blockack.md#appendix-a-standard-requirements-and-evidence) retains the common normative evidence.
+
+| Source and current behavior | Consequence |
+| --- | --- |
+| The PER operation in [IErrorModel](../../src/inet/physicallayer/wireless/common/contract/packetlevel/IErrorModel.h) accepts an SNIR object and signal part. It has no arbitrary range-length argument. | An aggregate calculation needs an explicit callable range boundary. A generic whole-DATA call cannot score each member. |
+| [Ieee80211ErrorModelBase](../../src/inet/physicallayer/wireless/ieee80211/packetlevel/errormodel/Ieee80211ErrorModelBase.h) declares protected header and DATA success calculations with a bit length. | The shared WLAN implementation can reuse those calculations for range probabilities. |
+| [NIST](../../src/inet/physicallayer/wireless/ieee80211/packetlevel/errormodel/Ieee80211NistErrorModel.h) and [Yans](../../src/inet/physicallayer/wireless/ieee80211/packetlevel/errormodel/Ieee80211YansErrorModel.h) derive from that WLAN base. | One base implementation can support both models and compatible custom subclasses. |
+| [BerTable](../../src/inet/physicallayer/wireless/ieee80211/packetlevel/errormodel/Ieee80211BerTableErrorModel.h) derives directly from the generic base. Its [initializer](../../src/inet/physicallayer/wireless/ieee80211/packetlevel/errormodel/Ieee80211BerTableErrorModel.cc) rejects HT operation strings at the local stage. | BerTable remains outside the HT aggregate profile. Its current HT refusal must remain distinct from the new role check. |
+| [Ieee80211Receiver](../../src/inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211Receiver.cc) delegates reception results to the common receiver path. | Aggregate reception needs its own outcomes before whole-DATA failure can discard valid members. |
+| [Ieee80211PhyHeader.msg](../../src/inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211PhyHeader.msg) has no modeled HT aggregation field. | A PHY property must distinguish an A-MPDU from a standalone MPDU. This also applies to a singleton aggregate. |
+| The HT methods in [Ieee80211PhyHeaderSerializer](../../src/inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211PhyHeaderSerializer.cc) write no fields and reconstruct an empty header. | P1 must extend the typed HT-SIG header and serializer together. A new MSG field alone cannot survive conversion to bytes. |
+| [Ieee80211Radio](../../src/inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211Radio.cc) validates some PHY headers but performs no HT CRC check. | Correct serialization alone cannot establish rejection of an invalid received HT-SIG. P2 needs receive validation before range outcomes. |
+| [MpduDeaggregation](../../src/inet/linklayer/ieee80211/mac/aggregation/MpduDeaggregation.cc) creates separate member Packets, but assumes valid lengths and uses the aggregate name. | Shared bounded parsing must replace unchecked range discovery. Feature A4 projects the PHY outcome onto each member Packet. |
+| [CommunicationCacheBase](../../src/inet/physicallayer/wireless/common/base/packetlevel/CommunicationCacheBase.cc) stores decision pointers per receiver, transmission, and signal part. It deletes replaced decisions and retained decisions at entry destruction. | The cache can own a derived WLAN decision. Separate signal-part slots need explicit sample ownership. |
+| [RadioMedium](../../src/inet/physicallayer/wireless/common/medium/RadioMedium.cc) caches decision lookups, but directly computes `isReceptionSuccessful()`. Its result path requests a WHOLE decision. | Aggregate queries must share cached samples across both paths and signal parts. Generic cache presence alone does not prevent repeated draws. |
+| Separate-part reception in [Radio](../../src/inet/physicallayer/wireless/common/radio/packetlevel/Radio.cc) queries PREAMBLE before HEADER. A failed PREAMBLE or HEADER removes the attempted reception. | Preserve the per-part checks without early HEADER or DATA calculation. That aborted attempt supplies no result to MAC. |
+| [FlatReceiverBase](../../src/inet/physicallayer/wireless/common/base/packetlevel/FlatReceiverBase.cc) attaches error-rate metadata through the old whole-PSDU calculation. [ReceiverBase](../../src/inet/physicallayer/wireless/common/base/packetlevel/ReceiverBase.cc) supplies power, SNIR, and reception times. | Aggregate result construction needs an explicit probability contract. It must preserve the other applicable receive metadata. |
+| [Rx](../../src/inet/linklayer/ieee80211/mac/Rx.cc) rejects a Packet bit-error flag before its FCS-mode branches. | A failed member can use its own Packet flag without a false checksum or shared chunk mutation. |
+| [Ieee80211Receiver](../../src/inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211Receiver.cc) sets its mode set at local initialization. [FlatReceiverBase](../../src/inet/physicallayer/wireless/common/base/packetlevel/FlatReceiverBase.cc) resolves its error model at that stage. | The receiver can check role presence at the physical-layer stage. That stage does not supply the exact limits for capability advertisement. |
+| [Ieee80211Mac](../../src/inet/linklayer/ieee80211/mac/Ieee80211Mac.cc) calculates effective channel widths and NSS at link-layer initialization, before its MIB update. | Exact coverage validation belongs at that handoff, with the same mode set and effective limits. |
+| [FlatReceiverBase](../../src/inet/physicallayer/wireless/common/base/packetlevel/FlatReceiverBase.cc) permits reception without an error model after its SNIR check. The [VHT/HT fixture](../../tests/module/Ieee80211VhtHtSupport_1.test) uses this path with transmit aggregation disabled. | An absent error model is a supported case. The new interface check must preserve it. |
+| [Ieee80211Radio](../../src/inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211Radio.ned) selects NIST by default for scalar and dimensional reception. | D1 needs the shared implementation before it enables the HT interface check. Otherwise, ordinary default HT configurations fail initialization. |
+| [Ieee80211Mib](../../src/inet/linklayer/ieee80211/mib/Ieee80211Mib.cc) derives local HT capability from the mode set and effective radio limits. This includes short GI. | Receive support must cover those advertised modes before capability publication. HT length exponent zero still advertises 8191 octets. |
+
+The feature plan and its human summary are local draft documents. Other local changes affect `AGENTS.md` and earlier Block Ack plans and records. This plan preserves those unrelated files. No source change or new runtime result supports the proposed behavior yet.
+
+## 3. Scope and acceptance criteria
+
+The scope is packet-level `Ieee80211Radio` with scalar or dimensional reception and HT mixed format at 20 MHz. Reception covers long GI and short GI at the advertised MCS and NSS values. The MAC feature's aggregate transmit policy remains limited to long GI. D1 supplies receiver integration, the shared NIST/Yans implementation, and basic compatibility checks. Deterministic probabilities establish its partial-reception evidence. D2 completes the NIST/Yans and compatibility evidence under the documented scalar SNIR abstraction.
+
+The plan owns PHY representation, shared bounded delimiter parsing, the optional aggregate error-model role, cached decisions, and receive indications. The MAC feature plan owns member selection, negotiated spacing, canonical MPDU ownership, release rules, responses, and recovery.
+
+The required results are:
+
+1. A decoded HT aggregation property survives radio encapsulation and decapsulation.
+2. A singleton aggregate retains aggregate identity.
+3. Header failure prevents valid aggregate content.
+4. A failed delimiter or member does not automatically discard every later recoverable member.
+5. Each outcome belongs to a bounded byte range in the actual PSDU.
+6. Repeated decision and result access causes no additional random draw.
+7. After successful header reception, whole-signal and separate-part reception produce consistent aggregate outcomes.
+8. Compatible ordinary MPDU reception preserves its calculations and random draw sequence.
+9. A present HT model without the aggregate role or required receive-mode coverage fails before capability advertisement, independently of local transmit aggregation.
+10. NIST and Yans share the WLAN base implementation without duplicate aggregate algorithms.
+11. Spacing delimiters receive outcomes, but alignment padding consumes no separate random draw.
+12. The indication permits a failed member Packet to retain its error without an incorrect whole envelope.
+13. The WHOLE summary does not replace the MAC's member validation or its complete receive outcome for contention.
+14. Non-HT custom models retain ordinary MPDU reception without the optional aggregate role.
+15. An absent error model retains PHY acquisition and SNIR checks without new error draws or an interface requirement.
+16. D1 preserves default NIST initialization, configured Yans initialization, and compatible ordinary MPDU reception before D2 extends the evidence.
+17. PREAMBLE queries preserve per-part acquisition checks without HEADER or DATA draws.
+18. HT-SIG serialization preserves length and aggregation for standalone, singleton, and multiple-member cases.
+19. Aggregate error-rate metadata follows the explicit probability contract in Section 4.3.
+20. Advertised HT20 short-GI reception has aggregate support even when the local transmit policy uses long GI or selects no aggregate.
+21. Invalid HT-SIG CRC or unsupported fields cause HEADER failure before delimiter or member draws.
+22. A valid header with insufficient modeled PSDU content causes bounded DATA refusal without range draws. Its HEADER result remains valid.
+
+This plan excludes correlated FEC errors, bit-level decoding, interference reconstruction within each member, and statistical accuracy or goodput claims. It cannot represent errors confined to alignment padding. It adds no MAC recovery timer, agreement store, or sealed Packet API change.
+
+## 4. Proposed design and reasons
+
+### 4.1 Representation and bounded ranges
+
+Extend the modeled HT PHY header with its aggregation property. The radio derives the receive indication from that property. A transmit-only tag or the member count cannot substitute for decoded state.
+
+HT-SIG is the HT signal field that carries PSDU length and other PHY control values. The packet-level model represents its fields without a waveform model.
+
+P1 owns the HT-SIG representation and [HT PHY header serializer](../../src/inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211PhyHeaderSerializer.cc). Extend the typed header and its serializer together. The existing `lengthField` remains the sole PSDU length value. The serializer maps that value to HT Length. New typed fields represent the other HT-SIG values, including Aggregation. The radio obtains mode-dependent values from the selected mode.
+
+IEEE Std 802.11-2024, Table 19-11 and Figure 19-6, define the fields and their positions. Clause 19.3.9.4.4 defines the HT-SIG CRC. Serialization must preserve the standard's field, reserved, CRC, and tail conditions. The encoded header must match the mode's 48-bit HT-SIG length. P1 must check golden bytes and independent reconstruction of standalone, singleton, and multiple-member cases. Full preamble waveform representation and VHT headers remain outside this profile.
+
+Current [radio encapsulation](../../src/inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211Radio.cc) already assigns 48 bits from the selected HT header mode. The [HT length calculation](../../src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211HtMode.cc) supplies that value despite the empty serializer. P1 preserves this declared length and completes byte conversion within it. Fresh decode must consume those same 48 bits. The [transmitter](../../src/inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211Transmitter.cc) calculates airtime from the selected mode and PSDU length. P1 preserves those duration inputs and calculations for ordinary HT MPDUs.
+
+In a hypothetical singleton case, the radio sets Aggregation to one and HT Length to the complete PSDU length. Conversion to bytes must preserve both values. A fresh decode must recover them without tags, names, or access to the original typed header. The recipient must therefore retain aggregate identity after that conversion. A typed-object copy alone cannot establish this result.
+
+#### HT-SIG receive validation
+
+The WLAN PHY owns one stateless HT-SIG validator beside the header representation and serializer. The serializer preserves the received CRC; decode must not replace it with a newly calculated CRC. The radio calculates the transmit CRC after all HT-SIG fields reach their final values. P1 defines this validator and its wire checks. P2 calls it at the receiver's HEADER decision boundary.
+
+At that boundary, the receiver checks CRC, supported field combinations, and agreement between encoded mode fields and the actual PHY mode. Invalid CRC or unsupported fields cause HEADER failure without delimiter or member draws. The radio reuses the validator before decapsulation relies on HT Length. On failure, decapsulation must not use the untrusted length for PHY trailer removal.
+
+An invalid header supplies no accepted Aggregation property. Separate-part reception uses the current abort; whole-signal reception uses the current failure-container path. MAC rejects that container before any MAC header or trailer peek. These header checks apply Clause 19.3.21 within the packet-level model without a new radio event or PHY primitive.
+
+A separate bounds check compares a valid header's HT Length with the available modeled PSDU before range calculation or length-based decapsulation. Insufficient content denotes malformed model input. The HEADER decision retains success and its decoded Aggregation property. The DATA decision reports failure with no range outcomes or range draws. The radio uses the current failure-container path without length-based trailer removal. MAC rejects that refused content before header or trailer peeks.
+
+This structural refusal does not represent physical signal loss. Clause 19.3.21 distinguishes invalid HT-SIG CRC from carrier loss after a valid header. The short-content fixture proves bounds safety only. It establishes no carrier-loss behavior or acceptance of a physically received prefix. Physical carrier-loss behavior remains outside this plan.
+
+Bad encoded bytes and probabilistic header loss remain separate test causes. In a hypothetical case, the error model reports header success, but the transmitted HT-SIG has an invalid CRC. The receiver rejects that header before any member draw or Block Ack response. Repeat the case without an error model. A successful probability result cannot override invalid encoded content.
+
+Extend the current aggregate decoder with bounded delimiter inspection that the WLAN receiver and MAC can reuse. Check every length, alignment interval, zero-length delimiter, CRC, and signature against the available PSDU. Each scan must advance. A malformed length cannot produce an accepted range or an out-of-bounds read.
+
+The shared parser supplies range facts, not delivery permission. The MAC must apply the received delimiter outcome before it trusts a boundary. A later recovered delimiter does not erase an earlier continuity break. The parser must not use names or a transmitter-private membership list to bypass corruption.
+
+The indication identifies each scored range by kind, byte offset, byte length, and outcome. It also retains common header success. A spacing delimiter has a four-octet delimiter range and no MPDU range. These values define the member-error projection before P2 starts.
+
+#### Scored ranges
+
+These choices define the packet-level error abstraction. They do not claim that the standard specifies independent error draws.
+
+| PSDU content | Probability and outcome rule |
+| --- | --- |
+| Each four-octet delimiter. | Score 32 bits once. This includes every zero-length spacing delimiter. Its failure breaks the applicable delimiter-continuity chain. |
+| Each nonzero MPDU range. | Score its complete header, payload, and FCS once. Its outcome belongs to that member only. |
+| Alignment padding. | Do not score it. It contributes airtime but has no error outcome or separate error draw. |
+
+Evaluate common acquisition and header success first. Header failure prevents all delimiter and member draws. After header success, process the bounded transmitted ranges in wire order: delimiter, member if present, padding, and the next delimiter. Probabilities zero and one give fixed outcomes without an RNG draw. An intermediate probability consumes one draw.
+
+A failed delimiter does not stop the physical calculation for known later ranges. MAC delimiter validation and bounded resynchronization still control which members it can accept. The range indication cannot authorize a jump through a failed delimiter.
+
+The wire fixture covers HT-SIG serialization, validation, delimiters, member limits, alignment, and bounded recovery. Feature A1 owns capability propagation and aggregate construction calculations for spacing, serialization, and member layout. Feature A3 owns dynamic member selection, spacing at the retained DATA mode, batch reduction, and canonical member ownership.
+
+### 4.2 Optional role and shared implementation
+
+Add a paired pure C++/NED WLAN contract for aggregate error probabilities. Its caller supplies the selected mode, modeled SNIR, and bounded delimiter or member ranges. The role distinguishes supported probabilities, invalid input, and unsupported aggregate reception. A pure mode-support query lets the receiver check required coverage before capability publication. That query performs no probability calculation or random draw.
+
+An implementation must support every advertised HT20 receive mode, whether it uses long GI or short GI. The MAC supplies its mode set, effective channel widths, and NSS limit to the receiver's coverage operation before the MIB update. These are the same values that the MAC supplies to `updateLocalHtCapabilities()`. Validation follows that operation's current MCS and GI rules. It adds no second capability store or independent filter. Unsupported input denotes modes or reception detail outside this contract.
+
+Table 9-224 defines short-GI support for reception of HT PPDUs. It supplies no separate short-GI permission for standalone MPDUs and aggregates. A model cannot advertise short-GI reception and refuse an otherwise valid short-GI aggregate. HT40 and VHT aggregate reception remain outside this plan. A later extension of advertised receive modes requires receiver support for those modes before advertisement.
+
+Keep the generic `IErrorModel` API free of WLAN dependencies. The receiver calls aggregate probability operations only for aggregate reception. When an error model is present, HT operation requires the role because advertised HT capability includes aggregate reception. Non-HT custom models retain their ordinary MPDU path without the new role. Custom HT models need the role even when local transmit aggregation remains disabled.
+
+This plan adds no aggregate role to `Ieee80211BerTableErrorModel`. Its current HT refusal precedes the new role check. The plan claims no loss of supported HT reception through BerTable. P0 records that boundary and a non-HT parent control. P2 compares the control and verifies the expected HT refusal. Tests for the new role diagnostic use the custom model, whose local initialization permits HT operation.
+
+Place the reusable probability calculation in `Ieee80211ErrorModelBase`. That implementation calls the current protected `getDataSuccessRate()` for each range length at the selected scalar SNIR. The pure contract contains no default method body. NIST, Yans, and compatible custom subclasses inherit the shared implementation. P2 includes this implementation before it enables the HT compatibility check. P3 extends its evidence rather than its interface availability.
+
+An absent error model retains the current error-free abstraction after PHY acquisition, possibility, attempt, and SNIR checks. After those checks succeed, the receiver records successful physical outcomes for bounded delimiter and member ranges without error-model calls or random draws. Delimiter CRC, bounds, and member FCS checks still control MAC acceptance. A failed common PHY check still prevents valid aggregate content. This case adds no substitute error-model module or support flag.
+
+The deterministic test model implements the same role with probabilities of zero or one. Thus, tests control error conditions while the production receiver still creates the reception outcomes. Models outside the WLAN base can supply their own implementation.
+
+The new role has concrete substitutes: the deterministic model and the shared WLAN implementation for NIST/Yans. A new callable boundary is necessary because the public generic API cannot accept an arbitrary member length. No synthetic reception object or temporary PHY length change supplies that absent argument.
+
+### 4.3 Decision ownership and random draws
+
+The current [medium lookup](../../src/inet/physicallayer/wireless/common/medium/RadioMedium.cc) computes a decision on a cache miss. The [cache implementation](../../src/inet/physicallayer/wireless/common/base/packetlevel/CommunicationCacheBase.cc) retains the decision's dynamic type. Its key contains the receiver, transmission, and signal part. A result lookup can request WHOLE after a DATA lookup. The direct success-query path can also bypass the decision cache.
+
+P0 must confirm these paths against the merged implementation base. Record cache insertion, replacement, entry destruction, and result consumption. The current `receivePacket()` consumes the cached result but leaves its decision entries. The sample guarantee applies for as long as those entries exist. The copied receive indication remains valid after cache cleanup.
+
+For aggregates, retain a PREAMBLE decision in its current cache slot. Its calculation preserves current per-part acquisition, possibility, attempt, and SNIR checks. It does not request HEADER or DATA. Shared WLAN NIST/Yans calculations add no PREAMBLE error draw. Direct PREAMBLE queries reuse that decision.
+
+HEADER first checks the cached PREAMBLE result before its own calculation. It retains common header success once the header decision boundary permits that calculation. An immutable WLAN decision in DATA retains the bounded delimiter and member outcomes, or structural refusal without ranges. A DATA lookup first obtains the cached HEADER result. A failed prerequisite or structural refusal prevents range draws. A WHOLE lookup derives its summary from these entries at the valid result boundary.
+
+| Query | Required calculation and retained result |
+| --- | --- |
+| PREAMBLE | Apply current per-part checks. Retain its decision without HEADER or DATA calculation. |
+| HEADER | Reuse PREAMBLE. Retain one header outcome at the header boundary. |
+| DATA | Reuse HEADER. Refuse insufficient modeled content before range draws. Otherwise, retain each bounded delimiter and member outcome once after successful header reception. |
+| WHOLE | Reuse prerequisite decisions. Derive the summary without another error draw. |
+
+The WHOLE slot owns a separate summary decision. It does not own the DATA pointer. Aggregate result construction reads the DATA outcomes and copies them into the WLAN indication. This design uses the current cache and adds no second outcome store.
+
+The WLAN receiver must route aggregate `computeIsReceptionSuccessful()` queries through those cached decisions. Its aggregate `computeReceptionDecision()` override constructs misses through calculation helpers. Construction must not call the cache-backed query recursively. Ordinary MPDU branches retain their current calculation and draw order.
+
+For aggregate DATA, bypass the inherited whole-PSDU error draw. WHOLE success requires common PHY success, valid PSDU bounds, and success for every scored range. This is a PPDU summary only. Aggregate result construction preserves the envelope's intact content after partial DATA failure. It must not invoke whole-Packet corruption or mark the envelope incorrect for one failed member.
+
+The WHOLE summary does not decide whether the MAC invokes EIFS. Feature A4 applies delimiter, member PHY, and FCS checks before it reports one aggregate outcome for contention. A correct member suppresses EIFS even when the WHOLE summary reports failure. The feature plan owns that rule and the preservation of valid HCF history after partial loss. [Aggregate receive outcome](ieee80211-ampdu-implicit-blockack.md#45-receive-validation-and-one-response-per-ppdu).
+
+The radio converts retained facts into a typed WLAN indication with copied values. It retains no pointer to a reception or transmission whose lifetime can end first. Feature A4 maps a failed complete member to that member's `Packet::setBitError(true)` before the normal Rx FCS predicate.
+
+In separate-part reception, a failed PREAMBLE or HEADER makes `Radio::continueReception()` abort the attempted reception. That attempt supplies no result or failure container to MAC. Existing radio state notifications let HCF invalidate its physical history. If whole-signal reception supplies a container after header failure, MAC rejects it before format-specific header or trailer peeks. Its indication reports failure without a decoded aggregation property. Both paths prevent aggregate acceptance, responses, range draws, and stale history without a new notification event.
+
+#### Aggregate error-rate metadata
+
+The current [result path](../../src/inet/physicallayer/wireless/common/base/packetlevel/FlatReceiverBase.cc) obtains `ErrorRateInd` through a whole-PSDU probability calculation. That estimate includes content outside the new scored ranges. It does not describe the selected range model. With a present error model, the aggregate result retains `ErrorRateInd` with `packetErrorRate`, `bitErrorRate`, and `symbolErrorRate` set to `NaN`. The typed WLAN indication separately supplies the retained outcomes. This first profile exposes no aggregate probability estimate.
+
+The [tag contract](../../src/inet/physicallayer/wireless/common/contract/packetlevel/SignalTag.msg) permits `NaN` for unknown probabilities. P0 must trace aggregate results through direct readers, [result filters](../../src/inet/common/ResultFilters.cc), and the [configured radio recorders](../../src/inet/physicallayer/wireless/common/radio/packetlevel/Radio.ned). The [network configurator](../../src/inet/networklayer/configurator/base/L3NetworkConfiguratorBase.cc) requires the tag for an ordinary probe; this read alone establishes no aggregate path. P0 must verify behavior with unknown values and absent tags for reachable consumers. A reachable incompatibility blocks P2's metadata implementation until the plan records its resolution. Tag omission requires evidence that all affected consumers accept absence.
+
+The aggregate result path must not call the old WHOLE probability operations to populate those fields. It also must not substitute a realized success or failure for a probability. Without an error model, preserve the common base's existing tag presence and exact values. Preserve applicable power, SNIR, reception times, transmission identity, mode, and channel metadata in both cases. Ordinary MPDU reception retains its current metadata calculations and values.
+
+In a hypothetical partial reception with a present model, MPDU 101 fails beside valid MPDUs 100, 102, and 103. The typed indication reports those distinct outcomes. The aggregate's probability fields remain `NaN` because the first profile supplies no aggregate estimate. A member must not inherit an aggregate probability as its own probability. The receiver needs no probability cache or additional error-model call for this metadata policy.
+
+### 4.4 Model detail and diagnostics
+
+The first abstraction has common PHY acquisition and header success, followed by independent delimiter and MPDU outcomes. Each range uses the current scalar SNIR reduction. The calculation adds no separate PHY header to every member.
+
+This abstraction does not model correlated FEC errors or variation of interference within a member. It cannot represent errors confined to alignment padding. Padding still contributes to PSDU length and PPDU airtime. More detailed error models remain separate work. NIST/Yans integration reuses current calculations; it does not establish new PHY accuracy.
+
+The WLAN receiver owns the HT compatibility operations. At `INITSTAGE_PHYSICAL_LAYER`, it rejects a present HT model if the aggregate role is absent. This stage follows local error-model initialization. Exact coverage validation occurs later, before the MAC updates local HT capability at `INITSTAGE_LINK_LAYER`. Both checks precede management capability advertisements and apply independently of the local transmit policy. Exponent zero cannot disable advertised A-MPDU reception because it represents 8191 octets under Table 9-225.
+
+The MAC calculates effective channel widths from transmitter and receiver support. It calculates the NSS limit from antenna count and its mode set. Before `updateLocalHtCapabilities()`, the MAC calls the receiver's pure coverage operation with those exact values and the same mode set. The receiver checks the HT20 MCS and GI combinations that those inputs advertise. Failure prevents the MIB update and capability publication. No probability call or random draw occurs.
+
+In a hypothetical configuration, one antenna limits advertisement to one spatial stream. The mode catalog also contains modes for more spatial streams. A model with complete support for the advertised modes must pass validation. Unsupported modes outside those effective limits must not cause rejection. This case prevents an incorrect coverage requirement for the full catalog.
+
+After initialization, `setModeSet()` checks role presence before it accepts an HT set. That setter alone cannot establish exact advertised-mode coverage. The current capability-update caller exists only in MAC initialization. Any later capability-update path must validate its exact inputs before the MIB changes local HT capability. This plan adds no runtime capability-update mechanism or second capability store.
+
+The check must not clear short-GI capability to obtain model compatibility. A present model must not substitute uniform success or one whole-PSDU result for the required aggregate outcomes. An absent model follows the explicit error-free case in Section 4.2. No new capability bit or mutable support flag represents this local compatibility condition.
+
+In a hypothetical configuration, a custom model supports ordinary HT MPDUs but lacks the aggregate role. The default transmit policy still selects no A-MPDU. Initialization must fail before any capability advertisement or association, rather than at the first received aggregate. The custom model needs an aggregate implementation before this HT configuration can run. The same model can retain its current non-HT MPDU operation without that role.
+
+In another hypothetical case, a custom model supplies the role but supports only long GI. The effective limits permit advertised HT20 short-GI reception. The mode-support query must cause initialization failure before the MIB update or advertisement. With complete support, a peer's short-GI aggregate must produce a valid PHY indication. Feature A5 separately verifies MAC receipt and the response. The local transmitter can retain long GI throughout this case.
+
+### 4.5 Reuse and justified additions
+
+| Addition | Responsible component and consumer | Reuse decision |
+| --- | --- | --- |
+| HT-SIG fields, serializer, validator, and typed indication. | WLAN radio and receiver validate header fields before MAC/Rx receives aggregate content. | Current PHY state cannot distinguish singleton aggregates. Its empty HT serializer cannot preserve the new property through bytes. Current radio validation omits HT CRC. |
+| Optional aggregate error-model role. | WLAN receiver checks receive-mode support before it calls deterministic or shared WLAN probability implementations. | Generic `IErrorModel` has no range-length argument. Interface presence alone cannot establish coverage of advertised receive modes. |
+| Shared probability implementation. | `Ieee80211ErrorModelBase` serves NIST, Yans, and compatible custom subclasses. | Current protected per-length calculations already supply the required arithmetic. |
+| Immutable aggregate decision. | Current medium cache owns outcomes for receiver and radio consumers. | Recomputed random decisions would change repeated observations. No second cache is necessary. |
+| Aggregate error-rate metadata. | WLAN receiver preserves applicable receive tags and reports unspecified probability fields through `NaN`. | The old whole-PSDU estimate describes a different calculation. This policy needs no new probability store. |
+| Bounded delimiter inspection. | Current decoder supplies common range rules to PHY and MAC. | Unchecked lengths cannot establish valid ranges or delimiter continuity. |
+| Early HT compatibility checks. | WLAN receiver checks role presence. MAC supplies exact advertised-mode inputs for receiver coverage validation before the MIB update. | PHY initialization lacks the effective advertisement limits. A runtime aggregate diagnostic occurs after capability advertisement. Exponent zero cannot represent absent aggregate reception. |
+| Explicit case without an error model. | WLAN receiver preserves current PHY checks and supplies bounded physical outcomes. | The current error-free abstraction needs no new role, module, or random draw. |
+
+These choices follow [minimal design](../../doc/project/rule/quality.md#qr-design-minimal), [reuse](../../doc/project/rule/architecture.md#ar-ext-reuse), and [contract purity](../../doc/project/rule/architecture.md#ar-org-contract-purity).
+
+## 5. Affected components and compatibility
+
+Changes affect MSG inputs for the HT PHY header and WLAN receive tag. They also affect the HT header serializer, receiver, radio, WLAN error-model contracts/base, NIST/Yans declarations, and aggregate decoder. The MAC adds the receiver coverage call before its current MIB update. P2 owns that required consumer with the receiver operation. Generated files follow the normal build procedure. Tests exercise the real medium and receiver.
+
+HT-SIG serialization completes byte conversion for ordinary HT MPDUs as well as aggregates within the current 48-bit header length. P0 records that declared length and the current mode/PSDU duration path. P1 verifies 48-bit serialization and decode; ordinary HT controls verify unchanged airtime under identical mode and PSDU inputs. Record byte changes separately from reception calculations, frame sequences, and random draws. Any fingerprint change needs its own causal explanation. Any affected recorded expectation follows the current baseline rules and authorization scope.
+
+D1 changes no default transmit aggregation policy. It includes the shared NIST/Yans implementation before the early HT check becomes active. D1 must retain default HT initialization and compatible ordinary MPDU reception. Its deterministic test model establishes partial-reception integration. Those deterministic results alone do not establish the full NIST/Yans profile; D2 completes that evidence.
+
+The early diagnostic changes compatibility for present custom HT models without the role or required receive-mode coverage. This includes configurations that previously used only ordinary MPDUs. Those configurations require model migration. Compatible ordinary MPDU calculations retain their current behavior and draws. Non-HT custom configurations need no aggregate implementation. Invalid encoded HT-SIG content now causes reception failure; valid ordinary HT content retains its current calculations.
+
+BerTable receives no HT support in this delivery. Its current initializer rejects `opMode="n"` and the HT operation strings that the radio supplies before the new role check. The migration guide lists this current limit and directs supported HT configurations to NIST or Yans. It must distinguish the current initializer error from the new diagnostic for an otherwise valid custom HT model. Non-HT BerTable controls compare initialization and reception with the pinned parent; they add no aggregate-role requirement. A failure in the parent remains a prior limitation, with no inferred compatibility pass.
+
+An absent error model remains valid for ordinary HT operation, including current `Ieee80211UnitDiskRadio` configurations. The receiver preserves their current PHY checks without error draws. The aggregate profile remains scalar or dimensional reception under the declared HT conditions. This compatibility requirement adds no aggregate support claim for unit-disk reception.
+
+The migration guide must identify the optional role, mode-support query, shared base, typed indication, range semantics, and early HT diagnostics. It must explain the MAC-to-receiver coverage handoff before the MIB update. It must distinguish absent, compatible, and incompatible models, such as a present role with incomplete receive-mode coverage. It must separate HT20 short-GI reception from the long-GI transmit policy. The current [seal registry](../../doc/project/audit/seal-list.md) protects `src/inet/common/packet/`. This plan uses its public API.
+
+The guide must explain HT-SIG byte conversion, header validation, per-part failure delivery, and the aggregate probability fields. It must distinguish structural PSDU refusal from header failure and physical carrier loss.
+
+General MAC stop/start recovery remains outside this plan. Current agreement reset and object destruction must safely release new transient metadata through their current owners.
+
+## 6. Implementation steps and dependencies
+
+Each step forms a buildable commit or short series with its required consumers and tests. D1 and D2 are separately reviewable deliveries. D1 supplies receiver integration without an intermediate loss of default HT operation. D2 completes the NIST/Yans and compatibility evidence for the declared profile.
+
+| Step and purpose | Responsible component and proposed change | Expected result and verification | Dependency |
+| --- | --- | --- | --- |
+| P0: Establish the merged base and cache contract. | Retain the agreed merge gate. Pin the integrated base. Trace all signal parts, direct success queries, result construction, failure delivery, and cache lifetime. Audit header length, the duration path, BerTable controls, and aggregate metadata consumers. | Record keys, insertion, replacement, destruction, result consumption, and shared-sample paths. Record the current 48-bit header length, duration inputs, and BerTable parent controls. Close the audit of aggregate metadata consumers before P2 implements its contract. Confirm error-model APIs and the feature-off baseline. | Read-only preparation can start now. Closure requires all three merges. |
+| P1: Define representation and scored ranges. | Extend typed HT-SIG state and its serializer together. Define the shared stateless validator. Define range kinds, offsets, outcomes, and member-error projection. Harden shared delimiter inspection. Fix scoring and evaluation order. | Golden bytes and independent decode preserve HT Length and Aggregation within the current 48-bit header length. Ordinary HT controls preserve airtime for identical mode and PSDU inputs. Wire checks distinguish invalid headers from insufficient modeled PSDU content. Other wire cases prove limits, truncation, alignment, and singleton identity. Spacing delimiters receive outcomes; padding has no outcome or draw. | P0. |
+| P2: Deliver receiver integration and basic compatibility. | Add the optional role, mode-support query, and shared WLAN implementation for NIST/Yans. Preserve reception without an error model. Include the role check and MAC coverage handoff with their required implementations. Apply HT-SIG validation at HEADER and structural content bounds at DATA. Add the deterministic model, per-part decisions, WHOLE summary, cache-backed queries, copied indication, and defined metadata. | Actual reception proves partial loss, delimiter failure, per-part failure delivery, query consistency, cache lifetime, and metadata without duplicate draws or probability calls. Invalid headers and structural refusal prevent range draws through distinct decisions. Advertised HT20 short-GI aggregates produce valid PHY indications. Default NIST, configured Yans, absent-model operation, and non-HT BerTable controls match their documented parent behavior. Incompatible present models fail before advertisement; BerTable's current HT refusal remains distinct. This closes D1 without MAC receipt or response acceptance. | P1. Close the metadata audit before its dependent implementation. Cache reuse, scored ranges, and member-error projection must be defined first. |
+| P3: Verify NIST/Yans aggregate outcomes. | Exercise the shared implementation from P2 through the real receiver. Compare range probabilities with the current per-length calculations. | Deterministic reference checks and fixed-seed runs prove cache use, repeatability, and supported model selection. No additional interface implementation is necessary to restore default HT initialization. | D1. Feature A3–A6 need not wait. |
+| P4: Close PHY compatibility evidence. | Complete migration text, affected receiver controls, and the execution record. Compare feature-off calculations and draws with the pinned parent. | NIST/Yans and unsupported-model cases match the documented boundary. Scoped checks close D2. Feature A7 requires D2. | P3. |
+
+```mermaid
+flowchart TD
+ M["Merged #1343, #1344, #1345
P0: Refresh the base"] --> P1
+ P1["P1: Representation and bounded ranges"] --> P2
+ P1 --> A1["Feature A1 and A3
Capabilities and preparation"]
+ P2["P2 / D1: Receiver integration
Shared models and basic compatibility"] --> A4
+ A1 --> A4["Feature A2 and A4 to A6
Reception, mandatory release, and exchanges"]
+ P2 --> P3["P3 and P4 / D2
NIST/Yans and PHY compatibility evidence"]
+ A4 --> A7["Feature A7
Document and verify the supported profile"]
+ P3 --> A7
+```
+
+D1 includes the shared NIST/Yans implementation and basic compatibility checks. D2 can follow deterministic MAC integration because it extends the evidence. That exchange still needs MAC receive step A4 and all applicable release conditions. Completion of the documented profile requires D2 and feature A7.
+
+## 7. Verification methods and expected results
+
+The PHY plan now owns three fixtures. P1 and P2 supplied the wire and partial-reception fixtures. P4 adds `Ieee80211AmpduPhyCompatibility_1.test` because its ordinary controls must compile on the parent without the new PHY interfaces. The feature plan owns seven proposed fixtures, so the combined inventory is ten. The execution record separates current PHY evidence from proposed MAC evidence.
+
+| Proposed fixture | Boundary and required observations |
+| --- | --- |
+| `Ieee80211AmpduWire_1.test`, unit/serializer. | HT-SIG golden bytes and independent decode preserve length, Aggregation, CRC, and the current 48-bit header length. Cover standalone, singleton, and multiple-member cases. Validator cases reject invalid CRC and unsupported field combinations. A separate bounds case refuses content shorter than valid HT Length. Real delimiter/member layouts prove CRC/signature, lengths 0/4095/4096, every alignment remainder, no final HT padding, truncation, and bounded resynchronization. |
+| `Ieee80211AmpduPhyReception_1.test`, module. | Actual reception proves all-good, first/middle/last member loss, regular/spacing delimiter failure, all-member failure, per-part header failure, and query consistency. It distinguishes HT-SIG rejection from structural DATA refusal before range draws. It also verifies copied indication lifetime, metadata with reachable consumers, and HT20 short-GI indications without MAC response acceptance. D1 checks NIST/Yans initialization, ordinary reception, absent-model operation, BerTable controls, and exact coverage at the MAC handoff. Absent roles and incomplete advertised coverage fail; unsupported unadvertised modes do not cause failure. D2 extends NIST/Yans aggregate evidence. |
+| `Ieee80211AmpduPhyCompatibility_1.test`, module. | The same source runs on the pinned parent and feature branch. NIST, Yans, absent-model, and non-HT custom controls expose exact outcomes, duration, metadata, draw counts, and subsequent random values. Three noise levels and seeds 0, 1, and 2 cover scalar and dimensional reception with whole and separate signal parts. |
+
+Use deterministic probabilities and seed 0 for the causal cases. Cover whole-signal and separate-part reception with scalar and dimensional models. Verify indication values after the producer's source objects leave their normal lifetime.
+
+Add a fixed probability between zero and one for cache checks. Record random draw counts before and after repeated reads. Those reads must add no draws. Count error-model calls to distinguish cached access from repeated probability calculations.
+
+Exercise PREAMBLE, HEADER, DATA, and WHOLE lookups, direct success queries, result creation, and repeated access in each supported reception configuration. At the production PREAMBLE boundary, verify no HEADER or DATA calculation or draw. Repeat that query without a new draw. Query WHOLE before DATA in a separate case at the valid result boundary. Both later lookup orders must use the same retained outcomes and add no duplicate draws. Verify result consumption and cache cleanup against P0's recorded ownership.
+
+Force PREAMBLE and HEADER failure separately through the production radio in separate-part mode. Verify that the failed attempt supplies no container to MAC. Verify no later range draws, response, accepted member, or retained physical history. In whole-signal mode, verify rejection of any delivered failure container before aggregate or ordinary MAC header parsing. These checks preserve the existing event boundaries and require no synthetic failure delivery.
+
+With a present model, check all three `ErrorRateInd` fields for `NaN` after aggregate result construction and repeated reads. Count the old WHOLE probability calls; aggregate metadata must add none. Check power, SNIR, reception times, transmission identity, mode, and channel at their existing boundaries. Without a model, compare tag presence and exact values with the current common-base behavior. Ordinary MPDU controls must retain their existing metadata. Member results must never present an aggregate probability as a member estimate.
+
+P0 records each metadata consumer, its aggregate reachability, and its behavior with unknown probabilities or an absent tag. The PHY module fixture exercises the reachable consumers that P0 identifies, with the configured filters and recorders. An unresolved incompatibility in P0 blocks P2's metadata implementation. A fixture failure blocks D1 until its documented resolution passes. Verify safe behavior with unknown values before D1 closes. Do not replace `NaN` with an absent tag without evidence that affected readers accept absence.
+
+P1's controls compare ordinary HT header length and PPDU duration with the pinned parent under identical mode and PSDU inputs. Verify the 48-bit header length before byte conversion and after independent decode. Verify unchanged PPDU airtime separately from changed bytes or fingerprint values. The wire fixture supplies representation evidence; the PHY module fixture verifies actual transmission duration. P1 adds ordinary HT duration controls to the PHY module fixture. P2 extends that fixture with partial-reception cases.
+
+The wire fixture must decode fresh HT-SIG bytes without access to the original typed header. Check Aggregation zero for standalone MPDUs and one for singleton or multiple-member aggregates. Check HT Length against the complete PSDU length. Golden bytes must also establish the reserved, CRC, tail, and field-position conditions in the cited standard. Compare the encoded length with the mode's HT-SIG length.
+
+Corrupt an encoded HT-SIG bit without repair of its CRC. Separately encode an unsupported field combination with a valid CRC. Run both cases through the production receiver with successful header probabilities and without an error model. Verify HEADER failure, no accepted Aggregation property, and no delimiter or member draws. Preserve the current separate-part abort and whole-signal failure-container paths.
+
+Separately supply a valid HT-SIG whose HT Length exceeds the available modeled PSDU. Verify HEADER success and preservation of the decoded Aggregation property. Verify DATA refusal with no range outcomes or draws. Verify bounded decapsulation and MAC refusal before header or trailer peeks in both reception modes. Repeat this case without an error model. This malformed-input check establishes no physical carrier-loss result.
+
+Vary alignment padding without a separate outcome or draw. Add zero-length spacing delimiters and verify their extra scored ranges. A failed spacing delimiter must preserve the continuity break after later recovery. The feature's A4 fixture separately proves rejection under declared-correct and computed FCS modes.
+
+Feature A4 also verifies EIFS suppression after partial reception and one corruption notification after total failure. Feature A5 owns production member finalization, computed-FCS retry checks, and aggregate cancellation during SIFS. The PHY wire fixture does not establish those MAC transmission results. [Feature acceptance fixtures](ieee80211-ampdu-implicit-blockack.md#proposed-direct-checks).
+
+P3 also compares range probabilities against each model's current per-length calculation. NIST/Yans runs use seeds 0, 1, and 2 to check invariants and reproducibility. These runs establish no statistical goodput or error-accuracy claim. Repeated cache access must leave later random draws unchanged.
+
+Feature-off controls compare compatible ordinary MPDU results and subsequent draws with the pinned parent under identical inputs. A non-HT custom model without the role must retain ordinary MPDU reception. Its HT configuration must fail initialization before any capability advertisement, also with the default transmit policy. After initialization, a change to an HT mode set without the required role must fail before receiver acceptance. The feature capability fixture separately proves the absence of management advertisements and association after the initialization failure.
+
+Use a custom model that exposes the role but supports only long GI. Use exact MAC inputs that permit short-GI advertisement. Verify initialization failure before the MIB changes local HT capability. The mode-support query must add no probability call or random draw. Also verify success with one antenna and unsupported modes outside the effective NSS limit. These checks must use the production MAC handoff and its exact mode set, channel widths, and NSS limit.
+
+With complete coverage, receive a valid HT20 short-GI aggregate through the production radio. D1 and feature A2 verify the copied PHY indication and member outcomes. Feature A5 separately verifies MAC receipt and one Compressed response after SIFS. The local aggregate transmit policy remains at long GI. Any later capability-update caller must use the same validation boundary; this plan adds no such runtime caller.
+
+D1 also checks ordinary HT reception with an absent error model and with each shared NIST/Yans implementation. Compare results and subsequent draws with the pinned parent. Retain the current VHT/HT fixture's absent-model configuration. Within the aggregate profile, an absent model produces successful physical ranges only after all common PHY checks succeed. Failed acquisition or SNIR checks still prevent valid aggregate content. Delimiter and FCS rejection remain active without a model, and no error draw occurs.
+
+The PHY module fixture includes a non-HT BerTable control and an expected HT refusal case. Supply a valid table file so that a file error cannot mask the operation-mode refusal. Compare the non-HT control with P0's parent evidence. Record any parent failure separately from feature regressions. Verify that BerTable's current error at local initialization precedes the new role check. Keep the custom HT model case as the direct proof of that new check.
+
+The planned commands use the current runners. They cannot establish a result before the fixtures exist:
+
+```sh
+make MODE=debug -j$(nproc)
+
+inet_run_unit_tests -m debug -f 'Ieee80211AmpduWire_1\.test$'
+
+inet_run_module_tests -m debug -f 'Ieee80211AmpduPhyReception_1\.test$'
+```
+
+Preview each selector with `--dry-run`. Record selected files, configurations, seeds, exact commands, HEAD, local source diff, library mode, verdicts, and artifacts. A zero-case selection is `NOT_RUN`. Compare the actual changed receiver dependencies before any broader campaign.
+
+Use the [gate procedure](../../doc/project/guide/run-the-gates.md) before publication under separate authorization. Coordinate final Wi-Fi coverage with the feature plan so one unchanged revision does not need duplicate broad runs. Release compilation remains required; debug behavioral evidence does not establish release behavioral coverage.
+
+The [feature plan's evidence status](ieee80211-ampdu-implicit-blockack.md#evidence-status) records this revision's document checks. No build, fixture, simulation, or noisy-channel campaign ran.
+
+## 8. Risks, decisions, and implementation scope
+
+The main risks are accidental whole-PSDU rejection, repeated random draws, unbounded delimiter parsing, and confusion between singleton aggregates and standalone MPDUs. D1 addresses those risks before MAC exchange integration.
+
+The scalar SNIR and independent-range assumptions are explicit model choices. Mandatory MAC release conditions remain in the feature plan. This split does not make those conditions optional.
+
+P1 owns HT-SIG representation and validation so that aggregate identity survives byte conversion without acceptance of invalid headers. P2 applies the validator through current HEADER failure paths. A separate DATA bounds check refuses malformed modeled content without a carrier-loss claim. The first profile reports aggregate outcomes through typed metadata and leaves probability estimates unspecified when an error model is present. PREAMBLE caching and current failed-header aborts preserve actual radio boundaries. These choices add no probability store, failure event, or expanded waveform model.
+
+Feature step A2 accepts D1. This plan owns the PHY implementation work, including the shared NIST/Yans implementation in P2. Broader NIST/Yans evidence can follow deterministic MAC integration. The documented profile requires D2; default HT operation must remain valid at D1.
+
+The full merge gate is the agreed delivery policy for one integrated S5 baseline. It keeps both plans on the same contracts and verification parent. Pure PHY calculations do not inherently require the MAC agreement lifecycle changes. An independent PHY base would need its own pinned dependencies and compatibility evidence.
+
+Implementation requires merged PRs [#1343](https://github.com/inet-framework/inet/pull/1343), [#1344](https://github.com/inet-framework/inet/pull/1344), and [#1345](https://github.com/inet-framework/inet/pull/1345). P0 must pin their integrated base. Read-only preparation does not satisfy that gate. A changed owner, result contract, or model-detail assumption requires a plan revision before the dependent implementation.
diff --git a/plan/pending/ieee80211-compressed-blockack-s4-execution.md b/plan/pending/ieee80211-compressed-blockack-s4-execution.md
new file mode 100644
index 00000000000..dc83901f766
--- /dev/null
+++ b/plan/pending/ieee80211-compressed-blockack-s4-execution.md
@@ -0,0 +1,178 @@
+# S4 execution record
+
+S4 completes Basic BAR work independently of DATA extraction. The initial source parent is `b29151fc8b`. The user authorized S4 implementation on 15 October 2026. Existing local edits to `AGENTS.md` and the prerequisite plans remain outside this change.
+
+## Implementation contract
+
+HCF previews the next eligible frame before a frame sequence extracts DATA. The originator ACK policy selects outstanding frames by peer and TID. The policy permits threshold deferral only for a candidate within that agreement's transmit window. The agreement owns the window start and size. Resolved DATA permits window progress; a received response number alone does not.
+
+HCF treats BAR work as contention work for its access category. Frame sequences permit BAR without a DATA pointer. Internal collision retains BAR work without DATA retry feedback. At an exchange boundary, HCF queues DELBA when an accepted agreement has no queued DATA, in-progress DATA, or active BAR exchange. Expiration uses the same teardown path after unresolved DATA disappears.
+
+The affected components are HCF, the QoS ACK policy, the ACK handler, the originator agreement, the in-progress queue, and HCF frame sequences. The optional QoS data-service contract exposes preview without a concrete dependency from the shared queue. Queue preview borrows a Packet pointer. Extraction retains the existing ownership transfer and sequence assignment. DCF extraction remains unchanged. S5 owns Compressed response validation and integration.
+
+The checks use focused module fixtures through HCF, frame sequences, Tx, and production response callbacks. They cover small windows, short bursts, peer/TID isolation, failed BAR recovery, collision, final expiration, and transfer completion. Timing checks compare Basic BAR exchanges with the pinned parent under single and multiple protection. A new unlawful continuation requires an admission correction before completion.
+
+The initial contract review checked source, ownership, protection, and callers before the first source edit. The affected source paths have no seal. S1 recipient progress and S3b teardown exist in the pinned parent.
+
+## Standards evidence
+
+The local corpus supplies IEEE Std 802.11-2024 normative Clauses 10.25.4, 10.25.6.7.1, and 10.23.2.9. Their canonical identifiers are `ieee80211-2024:clause:`. Their physical PDF pages are 2056, 2068–2070, and 2012–2014. Corpus status reports fresh input. The corpus linter result is `/tmp/s4-standard-lint.json`. Direct PDF inspection was unnecessary.
+
+## Verification
+
+Failed BAR recovery also requires the Retry bit on the next DATA attempt. Source inspection found that the old BAR failure path only changed ACK status. HCF now derives that bit from the failed Block Ack status before retransmission. IEEE Std 802.11-2024, Clause 9.2.4.1.6, physical PDF page 665, supplies this requirement for individual DATA.
+
+### Direct behavior checks
+
+The fresh debug build passes. The final focused selection passes 31 module fixtures and six unit fixtures. The module log is `/tmp/s4-module-final-limit.log`. The unit log is `/tmp/s4-unit-final-limit.log`. The new BAR fixture executes 30 scenarios across single and multiple protection. These results establish the recorded Basic paths. They do not establish Compressed support.
+
+The unit fixture checks peer/TID selection, sequence wrap, window boundaries, response identity, failed-response release, and exact lifetime eligibility. The production fixture exercises HCF, EDCA, frame sequences, Tx, and response callbacks. Its cases cover thresholds below five, accepted windows one and four, unrelated peers, negative bitmaps, timeout, and final expiration. The collision cases require BAR contention without a DATA retry entry. The nonzero contention-window case requires contention-window growth after collision. The short-limit case permits the initial Control-frame exception.
+
+BAR failure updates the access category's short retry counter and contention window. The one-failure limit case requires growth on the first failure. The unit fixture requires reset on the next failure. A successful Block Ack response resets both values. These checks require no DATA retry entry.
+
+The final-expiration case creates an incomplete fragment unit before two complete units. The production recipient retains those complete units behind the gap. Final expiration triggers DELBA. The recipient delivers both complete units before it deletes the agreement. This connects A5 to the existing recipient teardown path.
+
+Three existing fixtures require changed expectations because a short burst now resolves its final BAR and DELBA. The deferral fixture checks both remaining original DATA frames. Both retain their lifetime obligation after unsent disposal. The fragment-retirement fixture answers the unrelated peer's final BAR before its successor DATA. The multiple-protection fixture separates the DELBA management ACK from DATA ACKs.
+
+### Basic timing comparison
+
+The comparison parent is `b29151fc8b8953564dbbf61b48ece12658a1475c`. A detached checkout at `/tmp/inet-s4-parent` supplies its separately built debug library. The timing observer records actual transmit modes, preceding IFS, response airtime, TXOP deadline, and DATA count. The parent fixtures supply observations without S4 assertions. Their successful runner result alone supplies no S4 behavior proof.
+
+For the 1160-microsecond TXOP, the parent transmits a final BAR after five DATA frames. That BAR exchange ends at 2.270 ms. The deadline is 2.230 ms. This 40-microsecond overrun occurs with both protection modes. The S4 admission check rejects that continuation. EDCA later contends for a standalone BAR. The direct fixture requires every later BAR exchange to fit its applicable bounds.
+
+A separate case gives a standalone BAR a 50-microsecond TXOP limit. The initial BAR exchange takes 120 microseconds. Its 70-microsecond overrun uses the permitted initial Control-frame exception. It has no preceding DATA or Management frame. The admission check preserves this case.
+
+The diagnostic showcase traces also find DATA continuations beyond the TXOP bound in both parent and candidate. Each recorded violation ends with DATA. S4 does not repair general DATA admission. These observations limit the result to the changed Basic BAR paths. They do not establish general TXOP or NAV conformance.
+
+### Completion cost
+
+The cost fixture sends twelve UDP payloads of 700 bytes over a fixed 100-ms observation interval. One configuration sends one continuous burst. The other sends three bursts of four, with 20-ms gaps. Both configurations deliver all twelve payloads without a MAC drop. The payload goodput is 672000 bit/s in each configuration.
+
+| Candidate measurement | Continuous burst | Three bursts |
+| --- | ---: | ---: |
+| ADDBA requests / responses / DELBA frames | 1 / 1 / 1 | 3 / 3 / 3 |
+| Management airtime | 108 microseconds | 324 microseconds |
+| Total measured deferral | 280.133424 microseconds | 822.400272 microseconds |
+| Request contention | 61 microseconds | 156 microseconds |
+| Response time after request | 159.066712 microseconds | 486.200136 microseconds |
+| DELBA completion time | 114.066712 microseconds | 387.200136 microseconds |
+| Sum of payload delivery delays | 147.632334920 ms | 43.933601088 ms |
+
+The parent sends one request and one response in each configuration. It sends no completion DELBA. Its management airtime is 72 microseconds in each configuration. Both parent configurations deliver the same payload total. The parent and candidate also differ in extraction and BAR behavior. Thus, this comparison does not isolate an agreement-reuse cost. It supplies no general throughput or latency claim.
+
+### Regression and source checks
+
+The parent matches all nineteen original fingerprint rows. The evidence is `/tmp/s4-parent-fingerprints.log` and `/tmp/s4-parent-fingerprints-retry.log`. The selection preserves each original configuration, run number, simulation limit, and requested fingerprint ingredient. The final candidate run completes all nineteen rows in 227.330 seconds. Twelve rows match their baselines. Seven rows report fingerprint differences in configurations with Basic Block Ack enabled.
+
+The final log is `/tmp/s4-fingerprints-final-limit.log`. Its runner exits with status 1 because the seven recorded baselines differ. Every simulation reaches its required limit, including the enabled ad hoc row at ten seconds. Earlier attempts hit its 600-second CPU limit. Those partial attempts supply no replacement baseline. The final retry-limit correction preserves all nineteen results from the preceding full run.
+
+The following values are review candidates only. This execution does not approve or change a recorded baseline.
+
+| Configuration | Candidate fingerprint values |
+| --- | --- |
+| `NoFragmentation` | `4a83-1e6f/tplx;09db-9f72/~tNl;0596-3a23/~tND` |
+| `Fragmentation` | `91a8-1fb6/tplx;f43e-1f17/~tNl;aaae-de01/~tND` |
+| `MixedTraffic` | `202c-1e73/tplx;4e7c-be6a/~tNl;5544-6a00/~tND` |
+| `HCFfragblockack` | `140a-28bf/tplx;233d-c918/~tNl;5890-e570/~tND` |
+| TXOP `General` | `06db-3079/tplx;3727-bbbd/~tNl` |
+| Ad hoc `MacQos`, run 1 | `491a-01b6/tplx;9558-1598/~tNl;6970-8509/~tND` |
+| `MacQosWithBlockAck` | `cdb0-57dc/tplx;aa99-d171/~tNl;1368-030c/~tND` |
+
+The architecture check passes. The source-seal check permits every changed source path. The naming check reports the same 32 findings as the parent. The interface check retains the parent's two frame-step findings. The new QoS service interface passes that check. The logs use the `/tmp/s4-` prefix. No recorded fingerprint baseline changes belong to this implementation.
+
+The protocol selection executes eight fixtures. Four pass. Delayed Block Ack, Multi-TID Block Ack, and both A-MPDU fixtures retain four declared expected failures. `N_CompressedBa` selects Basic in this branch. Its pass does not establish Compressed behavior. The log is `/tmp/s4-protocol-final-limit.log`.
+
+The frame observer compares the first changed transmission in each affected configuration. It preserves actual bytes, sequence, TID, Retry, Duration, and selected mode. Its configuration adds an observer and a shorter observation limit. Thus, these observations explain local mechanisms; they do not replace the full fingerprint runs.
+
+| Configuration | First observed change | Confirmed mechanism |
+| --- | --- | --- |
+| `NoFragmentation` | ADDBA request replaces the next Normal Ack DATA at sequence 1. | Preview avoids speculative DATA extraction before setup. |
+| `MixedTraffic` | ADDBA request replaces the next large Normal Ack DATA at sequence 1. | The same extraction change permits setup before the successor. |
+| `Txop` | ADDBA request precedes the next large DATA at sequence 2. | The same extraction change preserves the setup boundary. |
+| `Fragmentation` | Final fragment 15 of sequence 1 reserves 464 instead of 116 microseconds. | Preview reads the queued unit before aggregation or fragmentation. |
+| `HCFfragblockack` | Retransmitted fragment 2 of sequence 1 sets Retry. | Failed Block Ack status now sets the DATA Retry bit. |
+| `MacQosWithBlockAck` | The first changed DATA has a 332-byte A-MSDU instead of a 166-byte unit. | Later extraction changes the available aggregation set. |
+| Ad hoc `MacQos`, run 1 | Fragment 2 of sequence 1 reserves 484 instead of 628 microseconds. | Preview no longer extracts the next aggregate for the Duration estimate. |
+
+These Duration observations do not prove complete NAV correctness. The existing Basic forecast does not model every BAR boundary. The direct timing assertions establish the separate BAR admission result.
+
+The saturated ad hoc case also exposes a host execution cost in transfer-completion checks. A temporary queue observer records 5120 predicate checks in each unrelated queue. Its stack trace identifies `completeTransfers` through `hasQueuedData`. HCF now checks the category from EDCA's existing TID mapping first. It retains the other categories as a fallback for custom classification. This changes search order without new persistent state or a different existence predicate.
+
+The temporary HCF observer records the same event count and fingerprint values before and after that search-order change. Its observed time in `processLowerFrame` falls from 41.0384 to 4.68354 seconds. Its observed time in `frameSequenceFinished` falls from 8.56017 to 0.226506 seconds. Host load differs between these observations. These values locate the cost; they supply no controlled speed benchmark.
+
+### Reproduction and retained evidence
+
+The local evidence directory is `audit/2026-10-15-s4/`. It retains the raw logs, parent observations, fingerprint selection, and temporary diagnostic sources. The diagnostic sources remain outside the delivered test suite. The directory also retains source hashes for this result. The build uses OMNeT++ from `/home/user/omnetpp-6.4.0aipre2` and the checkout's `setenv`.
+
+The category runners use these selections from the repository root:
+
+```sh
+make MODE=debug -j8
+inet_run_unit_tests -m debug -f 'Ieee80211(BlockAckScheduling|MultipleProtection|HtModeSet|HtModeCache|VhtHtCapabilities|ResponseModeSelection)_1\.test'
+inet_run_module_tests -m debug -f 'Ieee80211(BlockAck(ActionDelivery|ActionDisposal|ActionDispatch|AssociationReset|DataSelection|Deferral|DeferredCollision|FragmentRetirement|Lifecycle|Negotiation|RetiredData|Reordering|ResponseWait|Admission|Variant|Scheduling|CompletionCost)|EdcaDataLifetime(Arbitration|Aggregation|Congestion|Defaults)?|Hcf(Header|SingleProtection|MultipleProtection|InternalCollision)|BasicCompatibility|RecipientNav|SingleProtectionGroup|VhtHtSupport)_[12]\.test'
+inet_run_protocol_tests -p inet -m debug -w wifi -f '(N_BlockAck|Ac_BlockAck|N_Amsdu|N_CompressedBa|WifiQosDelayedBa|Ac_MultiTidBa|N_Ampdu|Ac_Ampdu)\.test$'
+```
+
+The fingerprint command runs from `tests/fingerprint`. The retained CSV contains the exact nineteen original rows.
+
+```sh
+./fingerprinttest -d -t4 -m '^/(showcases/wireless/(blockack|aggregation|fragmentation|qos|txop)|examples/(wireless|adhoc)/qos)/ ' -f tplx -f '~tNl' -f '~tND' -l /tmp/s4-fingerprints-final-limit.out /tmp/s4-fingerprint-final-limit.csv
+```
+
+### Scope limits
+
+S5 owns Compressed response integration. Delayed Block Ack, Multi-TID Block Ack, and A-MPDU remain outside this result. Recorded fingerprint baselines remain unchanged. The existing prerequisite plans and unrelated local files retain their prior edits. The initial implementation checks precede the commit and push request. The publication checks extend that initial evidence.
+
+## Initial checks before publication
+
+The user authorized commit and push on 15 October 2026. The series separates DATA preview from its first BAR selection consumer. The preview commit is `e2d20d6be6`. Its isolated debug build passes. Five focused module fixtures pass, including the direct preview checks for ownership and sequence assignment. The behavior commit contains BAR selection, window progress, transfer completion, and their direct fixtures.
+
+The final debug build passes. Its fresh selection passes 31 module fixtures and six unit fixtures. A separate scheduling run also passes after removal of the temporary parent-comparison directive. These checks retain the same runtime assertions. The logs are `/tmp/s4-publish-debug-build.log`, `/tmp/s4-publish-modules.log`, `/tmp/s4-publish-units.log`, and `/tmp/s4-publish-scheduling-clean.log`.
+
+The preview commit matches all nineteen retained fingerprint rows. Its log is `/tmp/s4-preview-fingerprints.log`. The final protocol rerun returns four passes and four declared expected failures across eight fixtures. Its log is `/tmp/s4-publish-protocol.log`.
+
+The final release compilation also passes. Its log is `/tmp/s4-publish-release-build.log`. The final fingerprint rerun completes nineteen rows. Twelve match their baselines; seven retain exactly the candidate values in this record. Its runner exits with status 1 for those differences. Its log is `/tmp/s4-publish-fingerprints.log`.
+
+The whole-tree architecture gate reports 36 findings. The whole-tree naming gate reports 23 findings. The interface gate reports 15 findings. Each sorted finding set matches the selected parent. The source-seal check passes.
+
+The series gates pass against `origin/master`; their two notes concern the earlier lifecycle commit `667ae51dc`. No pull request description check runs because this task creates no pull request.
+
+The C++ quality gate does not run because this checkout lacks `src/compile_commands.json`. Its setup exits with status 2. The log is `/tmp/s4-publish-cpp.log`. The compiler checks and the other gates do not replace this static analysis.
+
+## Rebase onto the updated S3b branch
+
+The user authorized this rebase after the initial publication. The pinned S3b target is `f6f12c5c9fed341f5834d4ccdeedeec7a562e102`. The original S4 head is `65804c762e524f64d07cfa336da0ceeb0d2bd425`. The backup ref is `refs/backup/compressed-blockack-s4-before-s3b-rebase-20261015`. The two S4 commits retain their order and purpose.
+
+The target adds local allocation indices for DATA retirement across large sequence gaps. The rebase retains that production mechanism alongside S4 window progress and completion DELBA. The collision fixture now records the extracted frame's allocation index. The fragment-retirement fixture retains all twenty gap scenarios and the Basic response path. A deferred successor keeps the earlier transfer open until the fixture advances the allocator. This prevents completion DELBA before the intended inactivity check.
+
+The user permits builds and tests only at the final commit. No intermediate commit receives a build or test. The earlier results above remain historical evidence. Final verification uses the last rebased commit. Its results and preserved-file hashes belong to `audit/2026-10-15-s4-rebase/`.
+
+## Approved fingerprints and final Basic boundary
+
+The user approved the seven final fingerprint changes on 2026-10-15 after review of their causes in PR #1344. This approval covers the non-graphical ingredients in the table below. Graphical `tyf` values remain unchanged and unverified. This section supersedes the earlier candidate values and the statements that S4 includes no baseline update.
+
+The final correction limits Basic single protection to an already prepared successor. HCF ends the TXOP before further DATA extraction can change aggregation, fragmentation, or agreement setup. Prepared fragments retain their continuation path. Final DATA in a zero-limit TXOP reserves only an immediate response when required. The policy can require more contention; it preserves standalone BAR progress.
+
+The source and tests at `8745330c1cc398f3dbc0a43be0c60cd7c2d5c529` contain this correction. Debug and release builds pass at that revision. All 31 module fixtures and six unit fixtures pass. Protocol execution reports four passes and four declared expected failures. The nineteen fingerprint simulations complete; twelve match the previous baselines and seven produce the approved values below. The raw evidence belongs to `audit/2026-10-15-s4-duration/`.
+
+The final observer shows zero Duration for sequence 1, fragment 15, in the fragmentation showcase. The TXOP ends with that DATA; BAR follows in a separate TXOP. The ad hoc observer shows 44 microseconds for sequence 1, fragment 2. Its ACK advertises zero Duration, and the TXOP ends. These observations replace the earlier unverified 464- and 484-microsecond forecasts. They do not establish general Basic DATA admission or complete NAV conformance.
+
+| Configuration | Approved fingerprints | Cause of the movement |
+| --- | --- | --- |
+| Block Ack `NoFragmentation`, run 0 | `9161-625d/tplx;6a02-09aa/~tNl;cbc0-3b0a/~tND` | Final Normal Ack DATA reserves only its ACK; setup and BAR exchange order also change. |
+| Block Ack `Fragmentation`, run 0 | `36f4-5c22/tplx;66b0-94cd/~tNl;45f3-af4f/~tND` | Final Block Ack DATA reserves zero; BAR obtains another TXOP. |
+| Block Ack `MixedTraffic`, run 0 | `351b-d4ba/tplx;5c1f-69a8/~tNl;6182-6da3/~tND` | The current reservation excludes the unextracted large successor; setup can precede that DATA. |
+| Fragmentation `HCFfragblockack`, run 0 | `2597-1e2e/tplx;d269-c6ae/~tNl;2f5c-4cca/~tND` | Failed Block Ack feedback sets Retry on retransmitted DATA; the final burst boundary also changes. |
+| TXOP `General`, run 0 | `ab91-87f3/tplx;f30c-6aa4/~tNl` | The reservation excludes an unextracted successor, and the frame sequence enforces that boundary. |
+| Ad hoc `MacQos`, run 1 | `4fd2-bb56/tplx;ecaf-89f7/~tNl;9398-0fae/~tND` | Final Normal Ack DATA reserves SIFS plus ACK; further extraction requires another TXOP. |
+| Wireless `MacQosWithBlockAck`, run 0 | `6edb-0326/tplx;2a8a-50ee/~tNl;4301-882d/~tND` | ADDBA reserves its immediate ACK without unrelated queued DATA; later extraction can change aggregation. |
+
+The final history keeps the preview preparation separate. One behavior commit contains BAR selection, the final Duration boundary, and these seven baseline rows. Its production source and tests match `8745330c1c`. This placement avoids an intermediate behavior commit with obsolete expectations. Final verification after the baseline update uses only the final commit, as requested.
+
+## Explicit agreement arguments
+
+BAR policy calls now require an explicit agreement handler through both the interface and concrete policy types. Transmitted-BAR processing also requires an explicit originator agreement. An omitted argument fails compilation instead of silently selecting behavior without agreement state. Explicit null values retain their documented behavior. Production callers already supply these arguments, so their runtime behavior stays unchanged.
+
+Compile-time checks cover omitted arguments through both policy types and the ACK handler. The scheduling fixture also checks explicit null policy calls with outstanding DATA. Custom test policies follow the same explicit contract. The migration guide describes the source compatibility change. The seven approved fingerprint rows stay unchanged.
+
+The source-equivalence statement in the preceding section describes baseline acceptance at `fcedba79e3`. This later correction changes API declarations, compile-time checks, and documentation. It changes no production C++ function body.
diff --git a/plan/pending/ieee80211-compressed-blockack-s5-execution.md b/plan/pending/ieee80211-compressed-blockack-s5-execution.md
new file mode 100644
index 00000000000..e89c9e74f7d
--- /dev/null
+++ b/plan/pending/ieee80211-compressed-blockack-s5-execution.md
@@ -0,0 +1,227 @@
+# S5 execution: Compressed Block Ack beside Basic compatibility
+
+S5 is complete through C1, C2, C3, and C4. The authorized TXOP correction removes the active Compressed timing blocker. HCF now defaults to Compressed. Explicit Basic compatibility remains available.
+
+The comparison parent is `75559a49661e33f727fa93a8c81c021596cfb4c9`. The active checkout is `/home/user/omnetpp_ws/inet-compressed-blockack-s5`. The maintainer requested S5 execution on 16 October 2026. That request authorizes C1 through C4 in the [replacement plan](ieee80211-compressed-blockack-replacement.md#compressed-replacement-steps).
+
+The checkout contained changes to `AGENTS.md` and four prerequisite plans before execution. It also contained an untracked planning history. This work preserves those changes. The pinned parent contains the S4 implementation. The [S4 execution record](ieee80211-compressed-blockack-s4-execution.md) supplies its earlier evidence and limits.
+
+## Implementation contract
+
+HCF owns the immutable profile and validates received frame formats before agreement updates. An active agreement identifies one peer and TID. A valid conflicting BAR stops the simulation because ADDBA does not negotiate the profile. Malformed input cannot cause that diagnostic, receipt updates, or agreement recovery.
+
+The recipient record owns explicit Compressed receipt bits and its scoreboard window. The reorder service owns delivery order and its separate window. Delivery must preserve receipt bits. An absent receipt produces zero, including positions before the first received DATA. Basic retains its fragment bitmap and existing compatibility behavior.
+
+The rate selector consumes the MIB rate sets. It selects a non-HT Compressed response and distinguishes initial BAR from later BAR. Frame sequences supply the actual request profile. Protection, timeouts, and duration calculations use the corresponding response length. Compressed responses contain 32 bytes with FCS; Basic responses contain 152 bytes.
+
+For an active Compressed agreement, the originator suppresses fragmentation for that peer and TID. The recipient rejects fragmented DATA before receipt or reorder updates. Normal Ack outside that agreement retains its existing fragmentation behavior.
+
+## Recipient interframe design
+
+The recipient must distinguish receipt evidence from the interframe relation. An agreement with start 100 and size four first receives Normal Ack DATA 100. Delivery advances the reorder start to 101. DATA 104 advances the scoreboard start to 101 and preserves receipt evidence for 100. A qualifying interframe relation permits delivery of 104 despite the gap. A false receipt bit or absent relation retains 104.
+
+Local receive endpoints alone cannot establish the ACK-to-DATA relation with propagation delay. The medium therefore supplies one copied snapshot of physical endpoints. The snapshot contains transmission identity, transmitter identity, transmission endpoints, and cached arrival endpoints. It retains no frame, transmission, arrival, or radio pointer.
+
+The PHY derives the interval at the current transmitter. For the same transmitter, it subtracts the preceding transmission end from the current transmission start. Otherwise, it subtracts the preceding signal's cached arrival end at the current transmitter. This calculation uses the propagation result that the medium already produced. It does not assume equal outward and return delay.
+
+HCF owns local event history. A pending candidate preserves the interval across PHY reception completion and later MAC delivery. A receiver-created transmission identifier connects those events. Unrelated receptions replace prior history. Failed, abandoned, and overlapping receptions must not preserve stale eligibility. Every local transmission clears prior history; completed ACK transmission can establish new history.
+
+SIFS recognition uses the reconstructed interval after signal extension. The current PHY model uses baseline timing without coverage-class adjustment. The permitted bound is one tenth of the slot time. Integer tick division rounds that bound inward. Physical propagation remains separate from this MAC timing parameter.
+
+RIFS recognition requires permitted HT operation, the same transmitter and RA, and no preceding SIFS response obligation. It uses the preceding PPDU endpoint without signal extension. Management must preserve the advertised RIFS Mode. VHT operation does not permit RIFS. This recipient rule adds no RIFS transmitter scheduler.
+
+The review found that ordinary HT transmission at 2.4 GHz omitted the six-microsecond signal extension. The correction belongs in the authoritative PHY duration. HCF must not add it again. Existing short-GI duration differences remain inherited PHY fidelity limits; they do not independently establish an interval error.
+
+## Standards evidence
+
+The standards source is IEEE Std 802.11-2024. The fresh local corpus is `/home/user/omnetpp_ws/inet/standards/processed`. The corpus lint output is `/tmp/inet-s5-standards-lint.json`.
+
+| Canonical node | Physical PDF pages | Requirement |
+| --- | --- | --- |
+| `ieee80211-2024:table:9-36` | 719–720 | BAR variant Type encoding. |
+| `ieee80211-2024:table:9-37` | 722–723 | Block Ack variant Type encoding. |
+| `ieee80211-2024:clause:10.25.6.3` | 2060–2061 | Full-state recipient receipt state. |
+| `ieee80211-2024:clause:10.25.6.6.2.1` | 2065–2066 | Recipient delivery and interframe conditions. |
+| `ieee80211-2024:clause:10.3.2.3.3` | 1889–1890 | SIFS definition and tolerance. |
+| `ieee80211-2024:clause:10.3.2.3.2` | 1888–1889 | RIFS definition and conditions. |
+| `ieee80211-2024:clause:10.22.5` | 1997–1998 | Coverage-class timing behavior. |
+| `ieee80211-2024:table:19-25` | 3493–3494 | Band-specific HT signal extension. |
+
+These provisions are normative. The selected corpus clauses resolve uniquely. The review inspected the original PDF for Clause 11.38.1 on physical page 2886. That clause requires a VHT AP to clear RIFS Mode. The corpus result without children does not include all later prose in that clause.
+
+## Verification boundary
+
+The initial full debug build and subsequent focused rebuilds pass. A build supplies no exchange or timing proof. The focused wire fixture passes independent octet checks. The constructed serializer fixture passes 278 cases. The HCF input fixture passes both configured profiles with supplied recipient agreements. Those results do not establish negotiation or PHY interval recognition.
+
+Five initial protocol simulations pass across the selected n/ac fixtures. They retain Basic coverage and add actual Compressed exchanges. This initial boundary precedes complete infrastructure timing classification and final interframe execution. Later sections record those results and the authorized correction.
+
+An isolated observer reproduces a DATA continuation overrun in the pinned parent. With a one-millisecond TXOP, the third DATA ends 178 microseconds after the bound. The candidate reaches the same endpoints. The parent also reproduces the first observed A-MSDU overrun. These observations identify inherited DATA admission limits. They do not establish general TXOP conformance or excuse a new Compressed violation.
+
+No recorded fingerprint baseline changed. This record does not authorize a baseline change or publication.
+
+## Confirmed timing blocker
+
+The candidate before the TXOP correction introduced a failing supported case that the exact parent did not reproduce. General run 6 uses HT operation, 24 Mbit/s DATA, single protection, aggregation, a one-millisecond TXOP, and seed zero. That candidate transmits two later A-MSDUs beyond their TXOP bounds. Each overrun is 310 microseconds. The affected TXOP contains two DATA frames, so the initial-MSDU exception does not apply.
+
+The second DATA ends at 1.014426 seconds. Its TXOP bound is 1.014116 seconds. The violation exists before any Block Ack response. The aggregate contains 1532 bytes in both versions. The exact parent has no aggregate overrun in run 6. Parent run 14 demonstrates a similar admission defect under different conditions. That other case does not make candidate run 6 acceptable.
+
+A direct run 6 reproduction also fails with the final reviewed debug library. Its output is `/tmp/inet-s5-reviewed-run6.out`; its error is `/tmp/inet-s5-reviewed-run6.err`. That run stops at the first later A-MSDU violation, at 1.014426 seconds. The earlier non-fatal observer campaign records both violations.
+
+The inherited admission mechanism causes the new result. `HcfFs` tests positive remaining time before a later exchange. It does not establish that the complete exchange fits. HCF excludes Block Ack support from its existing complete-cost DATA guard. The corrected HT control airtime and Compressed responses change the trajectory. The comparison does not isolate their separate contributions.
+
+All sixteen initial parent and candidate runs deliver thirty payloads, with 21000 payload bytes per run. All observed BAR/Block Ack exchanges remain within their applicable bounds. Those successes do not establish DATA containment. An independent review rejected that candidate under the [plan's timing condition](ieee80211-compressed-blockack-replacement.md#airtime-acceptance-and-admission-dependencies). The later correction and C3 acceptance below supersede that rejection.
+
+The [separate correction proposal](ieee80211-compressed-blockack-txop-admission.md) addresses preparation and admission. A final transmit guard alone cannot solve the problem. Aggregation already deletes constituent frames before HCF selects the actual mode. An oversized aggregate can therefore remain prepared and fail every later attempt. The proposal must preserve progress through ordinary MSDUs and the permitted initial-MSDU exception.
+
+## Focused verification after source review
+
+The reviewed debug library has SHA-256 `1635793563ff1c0912a4a0e55a70507879af2a20ef83a0b88502562748a347db`. Its build log is `/tmp/inet-s5-reviewed-debug-build.log`. The interframe fixture and direct run 6 reproduction use this library. The earlier mixed-interface regression batch is discarded.
+
+The unit and legacy results below use their recorded fresh libraries before the final bounded PHY review corrections. Those corrections repair empty-cache lookup, remove an unused virtual, and expose a watch. The protocol campaign uses library `1c57b8f562dbeb5cddbba49b77a3a676042fea7aef9b9e5578e1980c63109f56`. These earlier results retain their recorded source boundaries; they are not new executions against the final relink.
+
+| Check | Result | Evidence |
+| --- | --- | --- |
+| Focused units | Nine fixtures pass. | `/tmp/inet-s5-final-unit.log` |
+| HT Operation conversion | One fixture passes both RIFS permission values and operation equality checks. | `/tmp/inet-s5-ht-operation-unit.log` |
+| Legacy and HCF input modules | Thirty fixtures pass across the main batch and two repaired-fixture reruns. | `/tmp/inet-s5-final-module.log`, `/tmp/inet-s5-repaired-module.log` |
+| EDCA DATA lifetime modules | Nine fixtures pass. | `/tmp/inet-s5-lifetime-module.log` |
+| Production interframe fixture | All 28 runs pass with seed zero. | `/tmp/inet-s5-interframe-final/` |
+| Constructed serializers | All 278 cases pass. | `/tmp/inet-s5-serializer-roundtrip.log` |
+| n/ac protocol fixtures | Five simulations pass with Basic and Compressed coverage. | `/tmp/inet-s5-exchange-protocol-final.log` |
+| Lowest response mode | Two real infrastructure runs pass at 6 Mbit/s. | `/tmp/inet-s5-exchange-lowest-config.out` |
+
+The two legacy fixtures required valid synthetic frames. The Action-dispatch fixture now supplies the BAR trailer. The fragment-retirement fixture now supplies all 64 fragment bitmap entries. Their behavioral assertions remain intact.
+
+The interframe fixture supplies accepted agreements. It uses real PHY reception and HCF ACK completion. It verifies SIFS and RIFS bounds, sequence wrap, prior receipt truth, unrelated traffic, retirement, and cache expiry. Its BUSY control uses a signal below reception sensitivity but above energy detection. Its 168 malformed BAR receptions each leave zero pending candidates. Separate infrastructure tests establish real association and ADDBA; the interframe fixture does not claim negotiation coverage.
+
+The source reviewers report no remaining confirmed correctness defect in their bounded core and interframe scopes. Architecture gates pass for the changed MAC and PHY subtrees. Review supplies no runtime proof beyond the executed fixtures.
+
+## Extension contracts in this candidate
+
+These obligations describe the current candidate. They require migration guidance before publication. They do not change the C3 blocker or activate the default.
+
+| Contract | Obligation for a custom implementation |
+| --- | --- |
+| `IQosRateSelection::computeCompressedBlockAckReqMode()` | Select a legal non-HT BAR mode from the supplied initial-or-later TXOP position and current rate sets. |
+| `IRecipientQosAckPolicy::computeCompressedBlockAckDurationField()` | Subtract SIFS and the actual 32-byte response airtime from the request reservation. Preserve the Basic method. |
+| `IRecipientQosMacDataService::dataFrameReceived()` | Accept the new interframe argument. Preserve its meaning through recipient processing. A false value supplies no interval evidence. |
+| `QoSContext` | Supply HCF's resolved profile to the exchange. Existing callers retain the Basic default unless they explicitly supply Compressed. |
+| `RecipientBlockAckAgreement::setVariant()` | Set the profile during agreement creation. Do not change it during that agreement's lifetime. Compressed creation initializes explicit receipt state. |
+| `IRadioMedium` | Implement the new snapshot and gap queries with copied physical facts. Unavailable transmissions or arrival endpoints produce an absent result. |
+| `ICommunicationCache::findCachedArrival()` | Return a cached arrival without insertion, resizing, geometry recalculation, or other state changes. |
+| `IIeee80211Mode::getSignalExtensionTime()` | Supply the nominal extension for the mode. `Ieee80211ModeBase` supplies zero; ERP and HT override it where necessary. |
+
+## Common regression result
+
+All nineteen selected rows match their recorded non-graphical fingerprints. The runner uses debug mode and the original configuration, run, time limit, and requested non-graphical ingredients. It completes in 182.173 seconds with exit status zero. The selected input is `/tmp/inet-s5-fingerprint-selection.csv`; the log is `/tmp/inet-s5-fingerprint.log`.
+
+The command runs from `tests/fingerprint`:
+
+```sh
+./fingerprinttest -d \
+ -m '(showcases/wireless/(blockack|aggregation|fragmentation|qos|txop)|examples/(wireless/qos|adhoc/qos))' \
+ -f tplx -f '~tNl' -f '~tND' -t 6 \
+ /tmp/inet-s5-fingerprint-selection.csv
+```
+
+The graphical `tyf` ingredient remains unverified. No fingerprint baseline changed. These matches preserve the selected legacy regression evidence. They do not override the direct Compressed timing failure.
+
+These initial regression matches precede the separate TXOP correction and C4. No commit or publication occurred during this execution.
+
+## Build state before the authorized TXOP correction
+
+Both debug and release builds pass. The final release freshness check uses `make MODE=release -j6` from the repository root. Its log is `/tmp/inet-s5-reviewed-release-build.log`. The final debug build uses `make MODE=debug -j6`; its log is `/tmp/inet-s5-reviewed-debug-build.log`. The later NED edit corrects a comment and retains the Basic default.
+
+The separate TXOP proposal passed a consistency review as a scope request. On 2026-10-16, the maintainer authorized that correction and subsequent S5 completion. Source validation and independent review resolved the RTS boundary, lifetime ownership, and mode validation before implementation. The retained strict fixture preserves the inherited outside-agreement failure separately from active Compressed acceptance.
+
+## Authorized TXOP correction and C3 acceptance
+
+The [TXOP contract](ieee80211-compressed-blockack-txop-admission.md#resolved-implementation-contract) defines source ownership and each event boundary. The service creates duplicate previews without source removal. HCF selects one DATA mode, evaluates the aggregate, and evaluates an ordinary fallback with that same mode. Commitment transfers the same preview pointer only before DATA submission. Refusal preserves queue frames, deadlines, sequence state, and allocation state.
+
+RTS failure before DATA updates QSRC and the contention window without a provisional sequence counter. Original MSDU lifetimes bound repeated attempts. Expiry, capability loss, or changed response inputs cancel invalid preparation. Single protection reserves only the current exchange. A later BAR can require fresh contention. Basic and traffic outside active Compressed agreements retain their existing preparation and admission.
+
+Runtime verification found two effective selection callers that bypassed the first implementation. `RtsCtsFs` still extracted originals after admission. The multicast repeat predicate could also extract a prepared unicast successor before it ended the group sequence. Both callers now use the context-selected frame. Their focused regression cases preserve original queue state when no DATA submission occurs.
+
+The first retry policy required containment for all committed frames. A real BAR collision exposed a progress failure at 6 Mbit/s. One ordinary protected exchange needs 1.166 milliseconds under a nominal one-millisecond limit. Its first attempt qualifies for an exception, but conservative retry containment refused every later attempt. The implementation now uses the standard same-size single-MPDU exception with no earlier Data or Management frame. The supported path preserves committed MPDU size across retries.
+
+New previews clear Retry from the previous wireless hop. Committed retries derive Retry from local acknowledgment state. An oversized first-attempt A-MSDU still cannot use the retry exception. Internal collision commits only ordinary queued DATA before its retained recovery action. Multiple protection reserves the actual complete exchange for a permitted overrun.
+
+The corrected debug library has SHA-256 `0298dbd4c9625cd6f306865f5c1cfd0a3412230c6339b3a84289eb68eba713df`. The release library has SHA-256 `4b32335cdb2f558d60ada0761e46fde84c9879889e1c46b3c3f69bd11d9ae780`. Debug build 4 and the release build pass. Their logs are `/tmp/inet-s5-txop-debug-build-4.log` and `/tmp/inet-s5-txop-release-build.log`.
+
+The focused admission fixture passes all 24 cases with seed zero. It checks refusal, ordinary fallback, ownership, deadlines, sequence state, one mode query, and RTS cancellation. It also checks QSRC rollover, lifetime expiry, capability changes, internal collision, and multicast-to-unicast refusal. Cases 22 and 23 require actual ordinary and A-MSDU retries with unchanged size and DATA airtime above one millisecond. Evidence is `/tmp/inet-s5-admission24.out` and `/tmp/inet-s5-admission24.err`.
+
+All sixteen explicit-profile infrastructure cases pass the scoped timing assertions. The observer records agreement scope at DATA submission, before later teardown can change lookup results. Active Compressed checks include DATA with Normal Ack. No active later DATA or ACK overrun remains. All 98 matched BAR/Block Ack exchanges finish within their bounds. All 32 hop summaries report zero Block Ack overruns.
+
+The former run 6 failure now passes with zero DATA, ACK, or Block Ack overruns on both hops. Those hops transmit 23 and 11 active aggregates. The separate two-run lowest-response campaign records 29 actual 32-byte responses at 6 Mbit/s. Their airtime is 74 microseconds, with exactly 10 microseconds of SIFS. No Block Ack response exceeds its bound.
+
+The infrastructure observer separately records inherited violations outside active agreements. `StrictAllTiming` retains the original aggregate assertions and reproduces the pre-agreement aggregate failure in run 4. This acceptance establishes no timing compliance for those retained paths. The default change does not repair Basic or outside-agreement admission.
+
+The cleanup assertion permits only the idle `WAITING_FOR_SUCCESSOR` procedure with `SIMTIME_MAX` response deadline. Queues, in-progress frames, agreements, pending BAR state, and agreement timers must remain empty or inactive. That phase records a possible future setup after a completed Normal Ack transfer. It holds no unresolved exchange.
+
+Independent review accepts C3 for this scope and permits C4. The verdict is `/tmp/inet-s5-c3-acceptance-review.md`. Explicit-profile evidence is `/tmp/inet-s5-exchange-scoped-general16.out` and `/tmp/inet-s5-exchange-scoped-lowest2.out`. The scope table is `/tmp/inet-s5-exchange-scoped-general16.csv`. The unchanged strict failure remains in `/tmp/inet-s5-exchange-strict-all-inherited.out` and its `.err` file.
+
+## C4 default and migration verification
+
+`Hcf.ned` now defaults to Compressed. The infrastructure fixture omits the selector after C3 and requires actual Compressed frames. All sixteen default cases pass. Both lowest-response cases also pass after default activation. Evidence is `/tmp/inet-s5-default-exchange16.out` and `/tmp/inet-s5-default-lowest2.out`.
+
+All six external-style migration cases pass without an explicit selector. Legacy g and a links, and HT ad hoc links without peer records, deliver twelve UDP payloads through Normal Ack. Support-disabled controls produce no irrelevant warning. The fixture checks the resolved default, fallback diagnostics, absent ADDBA/BAR/Block Ack, and empty active state. Evidence is `/tmp/inet-s5-default-migration6.out`.
+
+All twelve initialization cases pass after default activation. They cover the omitted selector, explicit Basic, explicit Compressed, unknown values, and both delayed-policy guards. Evidence is `/tmp/inet-s5-default-variant12.out`. Retained Basic fragment, bitmap, and public API checks remain separate regression obligations.
+
+Both `N_BlockAck` and `Ac_BlockAck` also pass three consumer runs: explicit Basic, explicit Compressed, and the NED default. The third run uses bare INI `default`, which invokes OMNeT++ `acceptDefault()`. It verifies the resolved profile at all three HCF instances. Actual Compressed BAR/Block Ack and positive receipt bits complete that run. All six simulations and both diagnostic scripts pass. Evidence is `/tmp/inet-s5-default-consumer-protocol6.log` and `/tmp/inet-s5-default-consumer-{n3,ac3}.out`.
+
+Both final build freshness checks pass after the NED change. Their logs are `/tmp/inet-s5-c4-debug-build.log` and `/tmp/inet-s5-c4-release-build.log`. The C4 change affects the runtime NED default; the recorded debug and release library hashes remain unchanged.
+
+## Final regression evidence
+
+All final checks use the active checkout and seed zero where a simulation requires a seed. Debug mode retains runtime assertions. The release build supplies a separate compilation check. No fingerprint expectation changed.
+
+| Check | Final result | Evidence |
+| --- | --- | --- |
+| Unit fixtures | All ten pass. | `/tmp/inet-s5-c4-unit.log` |
+| Module regression batch | Forty-one pass initially. The retained Basic deferral fixture passes after its explicit profile correction. All 42 selected fixtures therefore pass across the batch and focused rerun. | `/tmp/inet-s5-c4-module-regression.log`, `/tmp/inet-s5-c4-deferral-module.log` |
+| Default infrastructure and migration | Both additional module fixtures pass: sixteen infrastructure cases and six migration cases. Both lowest-response cases pass. | `/tmp/inet-s5-default-module.log`, `/tmp/inet-s5-default-lowest2.out` |
+| Serializer | All 278 constructed cases pass, with zero unexpected or stale coverage entries. The separate golden-octet unit also passes. | `/tmp/inet-s5-c4-serializer.log`, `/tmp/inet-s5-c4-unit.log` |
+| Wi-Fi protocol files | Five supported files pass. Four deferred files retain expected failures. No unexpected result occurs. | `/tmp/inet-s5-default-plan-protocol9.log`, `/tmp/inet-s5-default-protocol-deferred.txt` |
+| Fingerprint rows | All nineteen selected rows match in 153.811 seconds. | `/tmp/inet-s5-c4-fingerprint.log` |
+| Architecture | All three scoped checks pass. | `/tmp/inet-s5-c4-architecture-mac.log`, `/tmp/inet-s5-c4-architecture-common.log`, `/tmp/inet-s5-c4-architecture-wifi.log` |
+
+The module union contains 44 distinct fixtures. It preserves Basic teardown, fragmentation, NAV, setup, association reset, response modes, and lifetime checks. It repeats the 28 physical interframe cases, 24 admission cases, and 76 scheduling cases after default activation. The scheduling fixture retains all original Basic cases. It adds actual Compressed shifted responses through active frame sequences and small-window arbitration.
+
+The recipient integration checks DATA 100, 102, and 101 through HCF. Delivery order is 100, 101, and 102. A subsequent BAR produces three positive receipt bits and zero holes after delivery. The six focused originator cases call the HCF control boundary directly. The scheduling fixture separately supplies actual response matching. These fixtures supply accepted agreements; the infrastructure and protocol fixtures prove negotiation separately.
+
+The legacy deferral fixture omitted a selector while it used non-HT mode `a`. The Compressed default correctly prevented its expected Basic setup. The fixture now selects Basic explicitly. All twelve original scenarios pass without assertion changes. This correction preserves the fixture's intended historical profile.
+
+The deferred protocol results remain explicit. `N_Ampdu` and `Ac_Ampdu` first miss an A-MPDU subframe. `Ac_MultiTidBa` passes both TID observations before it misses Multi-TID Block Ack. `WifiQosDelayedBa` observes a delayed request before recipient delayed acceptance fails. No expectation changed, and these results establish no support for those features.
+
+The interface gates report two MAC findings and three PHY findings. Their output is byte-identical to the pinned parent's output. The implicated source files remain unchanged. Logs are `/tmp/inet-s5-c4-interfaces-mac.log` and `/tmp/inet-s5-c4-interfaces-phy.log`, with corresponding `/tmp/inet-s5-parent-interfaces-*.log` files. This work adds no interface-gate finding.
+
+The final unit command runs from the checkout root:
+
+```sh
+inet_run_unit_tests -m debug -f '(Ieee80211BlockAck(Wire|Scheduling|ActionWire)_1|Ieee80211CompressedBlockAck(Rate|Record)_1|Ieee80211(ResponseModeSelection|ResponseTimeout|HtModeSet|HtCapabilities|VhtHtCapabilities)_1)\.test$'
+```
+
+The module regression command runs from the checkout root:
+
+```sh
+inet_run_module_tests -m debug -f 'Ieee80211(BlockAck.*|EdcaDataLifetime.*|PreparedCancellation_1|RecipientNav_1|Txnav_1|Hcf(SingleProtection_1|MultipleProtection_[12]|Header_1)|SingleProtectionGroup_1|VhtHtSupport_1|HtAssociation_1|Mgmt(StateCommit|RatePublication)_1|CompressedBlockAck(Input|Interframe|Admission|Originator)_1)\.test$'
+inet_run_module_tests -m debug -f 'Ieee80211BlockAckDeferral_1\.test$'
+inet_run_module_tests -m debug -f '(Ieee80211CompressedBlockAckExchange_1|Ieee80211CompressedBlockAckMigration_1|Ieee80211BlockAckVariant_1)\.test$'
+```
+
+The serializer and protocol commands run from the checkout root:
+
+```sh
+inet_run_serializer_tests -m debug -f 'serializer_chunk_roundtrip\.test$'
+inet_run_protocol_tests -m debug -w '^tests/protocol/wifi$' -f '(N_BlockAck|N_CompressedBa|N_Amsdu|N_Ampdu|Ac_BlockAck|Ac_BlockAckFallback|Ac_Ampdu|Ac_MultiTidBa|WifiQosDelayedBa)\.test$'
+inet_run_protocol_tests -m debug -w '^tests/protocol/wifi$' -f '(N_BlockAck|Ac_BlockAck)\.test$'
+```
+
+The final fingerprint command repeats the nineteen-row command above from `tests/fingerprint`. It uses the original configurations, runs, time limits, and selected non-graphical ingredients. Graphical `tyf` remains unverified. No CSV or other recorded baseline changed.
+
+Current user and migration guides describe the default, fallback, profile conflict, fragmentation boundary, and added interface obligations. Release notes preserve the supported `ac` path and all four deferred protocol requirements. The Basic showcase retains its explicit profile, topology, and result rows.
+
+The evidence remains limited to the recorded deterministic cases and seed. Production HT catalogs provide no distinct legal alternate response for the tested 32-byte frame. The selector unit verifies that alternate-rate contract separately. The recipient prefix release remains an interpretation of ordered delivery together with the individual-MPDU release condition. This work establishes no complete historical Basic conformance or complete VHT transfer conformance.
+
+The implementation checks above precede the separate commit, push, and pull request phase. The user authorized that phase after implementation. The original local plan changes and untracked history remain outside the publication scope.
diff --git a/plan/pending/ieee80211-compressed-blockack-txop-admission.md b/plan/pending/ieee80211-compressed-blockack-txop-admission.md
new file mode 100644
index 00000000000..6664e786d49
--- /dev/null
+++ b/plan/pending/ieee80211-compressed-blockack-txop-admission.md
@@ -0,0 +1,121 @@
+# TXOP prerequisite: admit Compressed DATA before destructive preparation
+
+This correction is complete and removes the active Compressed timing blocker. The [replacement plan](ieee80211-compressed-blockack-replacement.md#excluded-features-and-fragmentation) excludes complete-cost TXOP admission from its original scope. On 2026-10-16, the maintainer authorized this separate correction and subsequent S5 completion. The resolved contract below passed source validation and independent review before production edits. The [S5 execution record](ieee80211-compressed-blockack-s5-execution.md#authorized-txop-correction-and-c3-acceptance) supplies the final runtime evidence and scope limits.
+
+## Problem and required result
+
+The final S5 candidate sends two later A-MSDUs beyond their TXOP bounds in a supported case. The exact parent has no such violation in that case. The [S5 execution record](ieee80211-compressed-blockack-s5-execution.md#confirmed-timing-blocker) supplies the comparison and its limits. IEEE Std 802.11-2024, Clause 10.23.2.9 does not permit the observed continuation.
+
+The correction must establish the complete exchange cost before DATA commitment. An aggregate must fit its applicable bound. A later ordinary MSDU must also fit. The correction must retain the permitted initial unaggregated MSDU exception. It must not strand an oversized aggregate until lifetime expiry.
+
+A zero TXOP limit permits one SU PPDU with one MSDU or A-MSDU, plus its required protection and response. It does not mean zero permitted airtime. With a positive limit, the initial unaggregated MSDU exception permits at most one Data or Management frame. RTS and CTS airtime counts toward the exchange cost, but those Control frames do not increase that count. A same-size retransmission has a separate permitted exception under Clause 10.23.2.9; the implementation contract must state its retry policy.
+
+Hypothetical example: a TXOP has 200 microseconds left. The proposed aggregate needs 400 microseconds, while its first ordinary MSDU needs 150 microseconds. The service must commit that ordinary MSDU and leave the other MSDUs queued. If neither form qualifies, the sequence must end without queue removal or sequence assignment.
+
+The initial implementation scope covers active Compressed agreements. Explicit Basic and Normal Ack fallback outside an agreement retain their current preparation paths. This boundary avoids an unrelated compatibility repair. It does not claim that those retained paths satisfy every TXOP constraint.
+
+## Confirmed cause
+
+The [data service](../../src/inet/linklayer/ieee80211/mac/originator/OriginatorQosMacDataService.cc) removes aggregation candidates before HCF selects the actual DATA mode. The aggregation builder deletes the constituent frames. The service replaces their lifetime records and assigns a sequence number. At that point, a final HCF refusal cannot recover the original queue form.
+
+The [frame sequence](../../src/inet/linklayer/ieee80211/mac/framesequence/HcfFs.cc) tests whether positive TXOP time remains. That test does not prove that a complete later exchange fits. HCF excludes Block Ack support from its existing complete-cost DATA guard. A guard at final submission therefore needs a preparation correction as well.
+
+AARF and Onoe can change state when the rate selector queries them. Separate mode queries for a preview, RTS protection, and DATA submission can produce different decisions. The cost calculation must use the actual selected mode without extra rate-control queries.
+
+## Proposed ownership and sequence
+
+| Step | Responsible component and proposed action | Expected result and verification |
+| --- | --- | --- |
+| 1. Prepare without commitment. | The data service obtains borrowed candidates from the aggregation policy. It constructs a preview from duplicates. | Queue ownership, deadlines, and sequence counters remain unchanged. A focused fixture records them before and after refusal. |
+| 2. Select one DATA mode. | HCF selects the mode once for this preparation. The frame-sequence context owns that decision and the preparation state. | Aggregate evaluation, ordinary-MSDU evaluation, protection, and submission use one mode decision. A test rate control counts queries. |
+| 3. Calculate complete cost. | HCF derives the applicable IFS, DATA duration, protection, and immediate response from their existing owners. | Admission checks the TXOP bound and the applicable TXNAV bound. PHY mode objects supply durations. |
+| 4. Select a permitted form. | HCF first evaluates the aggregate. If it fails, HCF evaluates the selected ordinary MSDU with the same mode. | An oversized aggregate does not cause permanent refusal. The ordinary MSDU can fit or qualify for the initial-MSDU exception. |
+| 5. Defer when necessary. | The frame sequence ends normally when neither form qualifies. | The queue remains intact. Refusal causes no DATA retry outcome, sequence assignment, or aggregate lifetime replacement. |
+| 6. Commit at a safe boundary. | The data service commits only the accepted form. HCF uses its retained mode for the actual exchange. | Extraction, lifetime replacement, and sequence assignment occur once. No committed aggregate waits indefinitely because its form cannot fit. |
+
+BAR arbitration must precede DATA preparation, as S4 requires. The correction adds no BAR forecast. A final BAR can obtain a later TXOP through the existing arbitration path.
+
+The existing final false-transmit result remains a safety check. The frame-sequence handler removes a refused step and ends the sequence without a transmission-failure result. Normal admission refusal must occur before destructive source commitment.
+
+## Boundary requirements
+
+RTS creates an event boundary between preparation and DATA submission. A capability update, lifetime expiry, or RTS failure can occur before DATA starts. The implementation contract must identify the owner of each preview and original frame throughout that interval.
+
+The resolved contract retains reversible preparation until the first DATA submission. A failed RTS or invalidated capability discards the preview and its selected mode. Original queue frames retain their existing deadlines and sequence state. A later attempt obtains a new preparation and mode decision. The source trace established that this boundary fits the existing ownership and lifetime contracts.
+
+The frame-sequence API must distinguish preparation refusal from an empty queue and a failed transmission. `InProgressFrames` must not convert those outcomes into a DATA retry. The integration must preserve S4's preview and BAR-first contracts.
+
+Admission refusal causes no DATA retry outcome. An actual RTS failure retains its required recovery behavior while the service discards reversible preparation. The implementation contract must keep those paths separate.
+
+For DATA under an active Block Ack agreement, an actual RTS failure updates QSRC and the contention window. QSRC is the short retry counter for an access category. Clause 10.23.2.12.1 applies its associated-RTS increment rule to MSDUs and A-MSDUs outside a Block Ack agreement. A preview must not create a sequence-keyed counter before sequence assignment. QSRC rollover therefore does not discard queued DATA; its lifetime still bounds repeated attempts. This changes INET's former per-frame RTS discard behavior on this scoped path. Basic and Normal Ack outside an agreement retain their existing recovery.
+
+At the preceding exchange completion, TXNAV admission compares complete exchange cost strictly below the remaining TXNAV. That cost excludes the leading IFS. Positive-limit TXOP containment includes the leading IFS and permits equality with the bound. Clauses 10.23.2.8 and 9.2.5.2 support these separate comparisons. The correction must not move the strict TXNAV comparison to a later reference that rejects an exact single-protection reservation.
+
+The correction must not retain an illegal mode after peer capabilities change. It must also avoid a second adaptive-rate query merely to validate an unchanged prepared decision. The implementation contract must specify that validation separately from mode selection.
+
+These are engineering checks for the approved scope. They do not require the maintainer to choose a cache format or callback signature. If source analysis needs a different behavior or a wider compatibility change, the owner must update this proposal first.
+
+## Resolved implementation contract
+
+The data service owns duplicate previews through `PreparedQosData`. Each preparation records original frame IDs and its source queue. It retains no original frame pointer across events. `prepareFrame(queue, allowAggregation)` creates one form without extraction. HCF first requests the aggregate form. If that form fails admission, HCF requests the ordinary form with the same DATA mode.
+
+`QoSContext` owns the selected preparation and its retained modes. HCF supplies a preparation callback with three outcomes: `LEGACY`, `READY`, and `DEFERRED`. `HcfFs` calls it after BAR arbitration and before extraction. Refusal ends the repeat normally. Legacy contexts retain their existing extraction route.
+
+`isPreparationValid()` checks source membership, eligibility, deadlines, next sequence, and A-MSDU permission. HCF separately validates every retained mode and response input through pure queries. These checks include mode-set changes. A changed input cancels preparation without another adaptive DATA query. HCF repeats validation before RTS and before DATA submission.
+
+`commitPreparedFrame()` removes the accepted sources only before first DATA submission. It transfers the same preview pointer to `InProgressFrames`. Ordinary commitment transfers the original absolute deadline. Aggregate commitment starts the existing A-MSDU lifetime. Commitment assigns one sequence number and allocation index. Frame-sequence steps retain valid borrowed pointers until their disposal.
+
+```mermaid
+flowchart TD
+ Queue[Original MSDUs remain queued.] --> Preview[The service creates duplicate previews.]
+ Preview --> Admission{Does a permitted form qualify?}
+ Admission -->|No| Refuse[HCF ends the sequence without commitment.]
+ Admission -->|Yes| Protection{Does DATA require RTS?}
+ Protection -->|No| Validate{Are sources and retained modes valid?}
+ Protection -->|Yes| RTS[HCF submits RTS with the retained decision.]
+ RTS -->|CTS succeeds| Validate
+ RTS -->|CTS fails for valid sources| Recovery[Recovery updates QSRC and CW.]
+ RTS -->|Sources become invalid| Discard
+ Recovery --> Discard[Context disposal deletes the preview.]
+ Validate -->|No| Discard
+ Validate -->|Yes| Commit[The service commits the same preview pointer.]
+ Commit --> Data[HCF submits DATA with its retained mode.]
+```
+
+Expiry during CTS wait can remove originals safely because preparation stores their IDs. An invalid preparation ends without stale DATA or a synthetic RTS failure. A valid preparation with failed CTS causes one actual RTS recovery outcome. Context disposal deletes unused previews after step disposal.
+
+The scoped single-protection path reserves only its current exchange. It makes no forecast for unprepared successor DATA. A later BAR can contend through the existing S4 route. Multiple protection retains its reservation formula. This choice can increase contention and change simulation trajectories for active Compressed agreements.
+
+Committed retransmissions use the same-size single-MPDU exception from Clause 10.23.2.9 when no earlier Data or Management frame occurred in the TXOP. This exception can include a retransmitted A-MSDU. It does not permit an oversized first-attempt A-MSDU. The supported Compressed path has no fragmentation or A-MPDU operation. Its committed MPDU retains its size across retries; Retry and Duration field changes do not change that size. Basic and outside-agreement fallback retain their existing paths.
+
+A bounded LLDB observation established why conservative retry containment cannot satisfy the supported exchange. AP DATA at 6 Mbit/s needs a 1.166-millisecond protected exchange within a nominal one-millisecond TXOP. Its first ordinary transmission qualifies for the initial exception. A real BAR collision makes that DATA retry eligible. Conservative containment then refuses every attempt despite the permitted same-size exception. The root validated the required policy correction before source edits. Evidence is `/tmp/inet-s5-txop-diagnostic-lldb-cost.out`.
+
+Internal collision has a separate preparation entry. The existing HCF branch extracts DATA before it updates collision recovery. On the scoped queued path, HCF instead commits one ordinary preview before that existing recovery action. It must not create an aggregate before admission. Other queued MSDUs retain their ownership and deadlines. An unsent ordinary MSDU still qualifies for the first-transmission exception when its conditions hold.
+
+Multiple protection must reserve the actual exchange when a permitted initial ordinary MSDU or same-size retransmission exceeds a positive limit. The scoped path uses the existing helper's current-exchange form for that case. It must not reduce protection to zero merely because DATA exceeds the limit. The helper's reservation formula remains unchanged.
+
+The root validated this contract against the current source. An independent review found no unresolved blocking gap. This contract review supplies no runtime proof. Production implementation and focused verification follow this gate.
+
+## Proposed verification
+
+The direct reproduction uses `Ieee80211CompressedBlockAckExchange_1.test`, General run 6, seed zero. It selects HT operation, 24 Mbit/s DATA, single protection, aggregation, and a one-millisecond TXOP. The candidate must eliminate both 310-microsecond continuation overruns. The raw comparison remains in `/tmp/inet-s5-exchange-parent-candidate-comparison.json`.
+
+Add a focused preparation fixture at the proposed path `tests/module/Ieee80211CompressedBlockAckAdmission_1.test`. That fixture does not yet exist. Its cases must establish these results:
+
+- An initial aggregate that cannot fit becomes an ordinary MSDU before commitment.
+- A permitted initial unaggregated MSDU can exceed the TXOP limit under the applicable exception.
+- A smaller ordinary MSDU can replace an aggregate when it fits the remaining interval.
+- Refusal preserves queue ownership, deadlines, and sequence counters when neither form qualifies.
+- RTS failure, lifetime expiry, and capability changes discard invalid preparation without stale DATA transmission.
+- RTS protection, DATA duration, and admission consume one selected mode.
+- Basic controls preserve their existing behavior.
+
+Repeat S5's complete infrastructure campaign after the correction. Retain the lowest-response cases and exact BAR/Block Ack bounds. Repeat the affected lifetime, setup, frame-sequence, and fragmentation fixtures. Repeat the nineteen selected fingerprint rows without a baseline update.
+
+C3 can pass only after the supported corrected exchanges meet their applicable bounds. C4 and default activation must follow that result. This proposal grants no publication or fingerprint-baseline authority.
+
+## Execution result
+
+All 24 focused admission cases pass. All sixteen infrastructure cases pass the active Compressed timing assertions. Both lowest-response cases pass. Independent review accepts C3. C4 activates the default only after that decision and repeats its required checks.
+
+The former run 6 violation no longer occurs on either hop. Outside-agreement admission retains its inherited limits. `StrictAllTiming` preserves their separate reproduction. Basic settings and recorded fingerprint expectations remain unchanged. The final nineteen-row fingerprint campaign passes without a baseline update.
diff --git a/showcases/wireless/blockack/doc/index.rst b/showcases/wireless/blockack/doc/index.rst
index 1418ec4a985..b4808ed7c33 100644
--- a/showcases/wireless/blockack/doc/index.rst
+++ b/showcases/wireless/blockack/doc/index.rst
@@ -50,7 +50,7 @@ Block Acknowledgment in INET
In INET, the block ack mechanism can be enabled by setting the :par:`isBlockAckSupported`
parameter of the MAC's :ned:`Hcf` submodule to ``true`` (it is disabled by default).
-Select ``blockAckVariant = "basic"`` on both endpoints to retain the historical profile. Basic remains the default during preparation for Compressed support. This stage rejects ``"compressed"`` because its runtime exchange path does not yet exist. HCF rejects unknown selector values during initialization, also when Block Ack support is disabled.
+This showcase selects ``blockAckVariant = "basic"`` on both endpoints to retain the historical profile. HCF defaults to Compressed for other configurations. Compressed agreements require usable local and peer HT state. Without that state, DATA uses Normal Ack. HCF rejects unknown selector values during initialization, also when Block Ack support is disabled.
Several submodules of :ned:`Hcf` are involved in the block ack mechanism.
These modules have parameters controlling the block ack behavior. The following paragraphs
diff --git a/src/inet/linklayer/ieee80211/mac/Ieee80211DataSequenceTag.msg b/src/inet/linklayer/ieee80211/mac/Ieee80211DataSequenceTag.msg
new file mode 100644
index 00000000000..3d586aad6c0
--- /dev/null
+++ b/src/inet/linklayer/ieee80211/mac/Ieee80211DataSequenceTag.msg
@@ -0,0 +1,17 @@
+//
+// Copyright (C) 2026 OpenSim Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+
+import inet.common.INETDefs;
+import inet.common.TagBase;
+
+namespace inet::ieee80211;
+
+// Local allocation order before fragmentation. All fragments retain this index.
+// Unlike the wire sequence number, the index does not wrap at 4096.
+class Ieee80211DataSequenceTag extends TagBase
+{
+ int64_t allocationIndex = -1;
+}
diff --git a/src/inet/linklayer/ieee80211/mac/Ieee80211Frame.msg b/src/inet/linklayer/ieee80211/mac/Ieee80211Frame.msg
index e139deaf75d..7d1367944d4 100644
--- a/src/inet/linklayer/ieee80211/mac/Ieee80211Frame.msg
+++ b/src/inet/linklayer/ieee80211/mac/Ieee80211Frame.msg
@@ -43,6 +43,8 @@ const b LENGTH_ADDBAREQ = LENGTH_MGMT + B(9); // mgmt length + action body lengt
const b LENGTH_ADDBARESP = LENGTH_MGMT + B(9); // mgmt length + action body length
const b LENGTH_DELBA = LENGTH_MGMT + B(6); // mgmt length + action body length
const b LENGTH_BASIC_BLOCKACK = B(16 + 2 + (2 + 128) + 4); // header + ba control + ba information + fcs
+const b LENGTH_COMPRESSED_BLOCKACK = B(16 + 2 + 2 + 8 + 4); // header + control + sequence + bitmap + FCS
+const b LENGTH_BLOCKACKREQ = B(16 + 2 + 2 + 4); // header + control + sequence + FCS
const b DATAFRAME_HEADER_MINLENGTH = B(2 + 2 + 3 * 6 + 2); //bits without QoS, without Address4: 2 + 2 + 3*6(addresses) + 2
const b QOSCONTROL_PART_LENGTH = b(2 * 8); // QoS Control field length (bits)
const short int MAX_NUM_FRAGMENTS = 16;
@@ -356,7 +358,7 @@ class Ieee80211BlockAckReq extends Ieee80211TwoAddressHeader
class Ieee80211BasicBlockAckReq extends Ieee80211BlockAckReq
{
- chunkLength = B(16 + 2 + 2); // header + BAR control + BAR information
+ chunkLength = LENGTH_BLOCKACKREQ - B(4);
int tidInfo @bit(4); // The TID_INFO subfield of the BAR Control field of the Basic BlockAckReq frame contains the TID for which a Basic BlockAck frame is requested.
@@ -373,7 +375,7 @@ class Ieee80211BasicBlockAckReq extends Ieee80211BlockAckReq
class Ieee80211CompressedBlockAckReq extends Ieee80211BlockAckReq
{
- chunkLength = B(16 + 2 + 2); // header + BAR control + BAR information
+ chunkLength = LENGTH_BLOCKACKREQ - B(4);
int tidInfo @bit(4); // The TID_INFO subfield of the BAR Control field of the Compressed BlockAckReq frame contains the TID for which a BlockAck frame is requested.
// The BAR Information field of the Compressed BlockAckReq frame contains the Block Ack Starting
@@ -455,7 +457,7 @@ class Ieee80211BasicBlockAck extends Ieee80211BlockAck
//
class Ieee80211CompressedBlockAck extends Ieee80211BlockAck
{
- // chunkLength TODO
+ chunkLength = LENGTH_COMPRESSED_BLOCKACK - B(4);
multiTid = 0;
compressedBitmap = 1;
diff --git a/src/inet/linklayer/ieee80211/mac/Ieee80211Mac.cc b/src/inet/linklayer/ieee80211/mac/Ieee80211Mac.cc
index b864c39f435..a372f92a72c 100644
--- a/src/inet/linklayer/ieee80211/mac/Ieee80211Mac.cc
+++ b/src/inet/linklayer/ieee80211/mac/Ieee80211Mac.cc
@@ -34,6 +34,13 @@
namespace inet {
namespace ieee80211 {
+void Ieee80211Mac::resetPeerAgreements(const MacAddress& peer)
+{
+ Enter_Method("resetPeerAgreements");
+ if (hcf)
+ hcf->resetPeer(peer);
+}
+
using namespace inet::physicallayer;
Define_Module(Ieee80211Mac);
@@ -83,6 +90,7 @@ void Ieee80211Mac::initialize(int stage)
if (transmitter->isHtChannelWidthSupported(channelWidth) &&
receiver->isHtChannelWidthSupported(channelWidth))
operationalChannelWidths.insert(channelWidth);
+ receiver->validateHtReceptionCapabilities(modeSet, operationalChannelWidths, operationalHtSpatialStreamLimit);
}
mib->updateLocalHtCapabilities(modeSet, operationalChannelWidths, operationalHtSpatialStreamLimit);
emit(modesetChangedSignal, modeSet);
diff --git a/src/inet/linklayer/ieee80211/mac/Ieee80211Mac.h b/src/inet/linklayer/ieee80211/mac/Ieee80211Mac.h
index 25d1b25fe65..0b28e4a8bc0 100644
--- a/src/inet/linklayer/ieee80211/mac/Ieee80211Mac.h
+++ b/src/inet/linklayer/ieee80211/mac/Ieee80211Mac.h
@@ -11,6 +11,7 @@
#include "inet/common/ModuleRefByPar.h"
#include "inet/linklayer/base/MacProtocolBase.h"
#include "inet/linklayer/ieee80211/mac/contract/IDs.h"
+#include "inet/linklayer/ieee80211/mac/contract/IBlockAckAgreementLifecycle.h"
#include "inet/linklayer/ieee80211/mac/contract/IRateControl.h"
#include "inet/linklayer/ieee80211/mac/contract/IRateSelection.h"
#include "inet/linklayer/ieee80211/mac/contract/IRx.h"
@@ -35,7 +36,7 @@ class Ieee80211MacHeader;
* exact operation of the MAC depend on the plugged-in components (see IUpperMac,
* IRx, ITx, IContention and other interface classes).
*/
-class INET_API Ieee80211Mac : public MacProtocolBase
+class INET_API Ieee80211Mac : public MacProtocolBase, public IBlockAckAgreementLifecycle
{
public:
static simsignal_t frameTransmissionOutcomeSignal;
@@ -106,6 +107,7 @@ class INET_API Ieee80211Mac : public MacProtocolBase
virtual void sendUpFrame(Packet *frame);
virtual void sendDownFrame(Packet *frame);
virtual void sendDownPendingRadioConfigMsg();
+ virtual void resetPeerAgreements(const MacAddress& peer) override;
virtual void processUpperFrame(Packet *packet, const Ptr& header);
virtual void processLowerFrame(Packet *packet, const Ptr& header);
diff --git a/src/inet/linklayer/ieee80211/mac/Ieee80211MacHeaderSerializer.cc b/src/inet/linklayer/ieee80211/mac/Ieee80211MacHeaderSerializer.cc
index 2a482cded2f..d2b1ae642fe 100644
--- a/src/inet/linklayer/ieee80211/mac/Ieee80211MacHeaderSerializer.cc
+++ b/src/inet/linklayer/ieee80211/mac/Ieee80211MacHeaderSerializer.cc
@@ -331,10 +331,13 @@ void Ieee80211MacHeaderSerializer::serializeFields(MemoryOutputStream& stream, c
stream.writeMacAddress(blockAckReq->getTransmitterAddress());
bool multiTid = blockAckReq->getMultiTid();
bool compressedBitmap = blockAckReq->getCompressedBitmap();
- // BAR Control is a 16-bit little-endian field: bit 0 BAR Ack Policy, bit 1
- // Multi-TID, bit 2 Compressed Bitmap, bits 3-11 reserved, bits 12-15 TID_INFO
+ // IEEE Std 802.11-2024, 9.3.1.7: B1-B4 encode the complete BAR Type.
+ if (!multiTid && !compressedBitmap && (blockAckReq->getReserved() & 3))
+ throw cRuntimeError("Basic BAR reserved fields must encode Type 0");
uint16_t barControl = (blockAckReq->getBarAckPolicy() ? 1 : 0) | ((multiTid ? 1 : 0) << 1)
| ((compressedBitmap ? 1 : 0) << 2) | ((blockAckReq->getReserved() & 0x1FF) << 3);
+ if (!multiTid && compressedBitmap)
+ barControl = 2 << 1;
if (!multiTid && !compressedBitmap) {
auto basicBlockAckReq = CHK(dynamicPtrCast(chunk));
stream.writeUint16Le(barControl | ((basicBlockAckReq->getTidInfo() & 0xF) << 12));
@@ -347,6 +350,8 @@ void Ieee80211MacHeaderSerializer::serializeFields(MemoryOutputStream& stream, c
}
else if (!multiTid && compressedBitmap) {
auto compressedBlockAckReq = CHK(dynamicPtrCast(chunk));
+ if (compressedBlockAckReq->getFragmentNumber() != 0)
+ throw cRuntimeError("Compressed BAR Fragment Number must be zero");
stream.writeUint16Le(barControl | ((compressedBlockAckReq->getTidInfo() & 0xF) << 12));
writeSequenceControl(stream, compressedBlockAckReq->getFragmentNumber(), compressedBlockAckReq->getStartingSequenceNumber().get());
if (stream.getLength() - startPos != compressedBlockAckReq->getChunkLength())
@@ -367,10 +372,13 @@ void Ieee80211MacHeaderSerializer::serializeFields(MemoryOutputStream& stream, c
stream.writeMacAddress(blockAck->getTransmitterAddress());
bool multiTid = blockAck->getMultiTid();
bool compressedBitmap = blockAck->getCompressedBitmap();
- // BA Control is a 16-bit little-endian field: bit 0 Ack Policy, bit 1 Multi-TID,
- // bit 2 Compressed Bitmap, bits 3-11 reserved, bits 12-15 TID_INFO
+ // IEEE Std 802.11-2024, 9.3.1.8: B1-B4 encode the complete BA Type.
+ if (!multiTid && !compressedBitmap && (blockAck->getReserved() & 3))
+ throw cRuntimeError("Basic Block Ack reserved fields must encode Type 0");
uint16_t baControl = (blockAck->getBlockAckPolicy() ? 1 : 0) | ((multiTid ? 1 : 0) << 1)
| ((compressedBitmap ? 1 : 0) << 2) | ((blockAck->getReserved() & 0x1FF) << 3);
+ if (!multiTid && compressedBitmap)
+ baControl = 2 << 1;
if (!multiTid && !compressedBitmap) {
auto basicBlockAck = CHK(dynamicPtrCast(chunk));
stream.writeUint16Le(baControl | ((basicBlockAck->getTidInfo() & 0xF) << 12));
@@ -386,6 +394,10 @@ void Ieee80211MacHeaderSerializer::serializeFields(MemoryOutputStream& stream, c
}
else if (!multiTid && compressedBitmap) {
auto compressedBlockAck = CHK(dynamicPtrCast(chunk));
+ if (compressedBlockAck->getFragmentNumber() != 0)
+ throw cRuntimeError("Compressed Block Ack Fragment Number must be zero");
+ if (compressedBlockAck->getBlockAckBitmap().getSize() != 64)
+ throw cRuntimeError("Compressed Block Ack bitmap must contain 64 bits");
stream.writeUint16Le(baControl | ((compressedBlockAck->getTidInfo() & 0xF) << 12));
// Block Ack Starting Sequence Control = 4-bit Fragment Number (reserved) + 12-bit sequence number
writeSequenceControl(stream, compressedBlockAck->getFragmentNumber(), compressedBlockAck->getStartingSequenceNumber().get());
@@ -600,6 +612,7 @@ const Ptr Ieee80211MacHeaderSerializer::deserializeFields(MemoryInputStre
blockAckReq->setReceiverAddress(stream.readMacAddress());
blockAckReq->setTransmitterAddress(stream.readMacAddress());
uint16_t barControl = stream.readUint16Le();
+ auto barType = (barControl >> 1) & 0xF;
bool multiTid = (barControl >> 1) & 1;
bool compressedBitmap = (barControl >> 2) & 1;
blockAckReq->setBarAckPolicy(barControl & 1);
@@ -608,7 +621,7 @@ const Ptr Ieee80211MacHeaderSerializer::deserializeFields(MemoryInputStre
blockAckReq->setReserved((barControl >> 3) & 0x1FF);
int fragmentNumber = 0;
SequenceNumberCyclic startingSequenceNumber;
- if (!multiTid && !compressedBitmap) {
+ if (barType == 0) {
auto basicBlockAckReq = makeShared();
copyBasicFields(basicBlockAckReq, macHeader);
copyBlockAckReqFrameFields(basicBlockAckReq, blockAckReq);
@@ -618,14 +631,18 @@ const Ptr Ieee80211MacHeaderSerializer::deserializeFields(MemoryInputStre
basicBlockAckReq->setStartingSequenceNumber(startingSequenceNumber);
return basicBlockAckReq;
}
- else if (!multiTid && compressedBitmap) {
+ else if (barType == 2) {
auto compressedBlockAckReq = makeShared();
copyBasicFields(compressedBlockAckReq, macHeader);
copyBlockAckReqFrameFields(compressedBlockAckReq, blockAckReq);
+ compressedBlockAckReq->setBarAckPolicy(false);
+ compressedBlockAckReq->setReserved(0);
compressedBlockAckReq->setTidInfo((barControl >> 12) & 0xF);
readSequenceControl(stream, fragmentNumber, startingSequenceNumber);
compressedBlockAckReq->setFragmentNumber(fragmentNumber);
compressedBlockAckReq->setStartingSequenceNumber(startingSequenceNumber);
+ if (fragmentNumber != 0)
+ compressedBlockAckReq->markIncorrect();
return compressedBlockAckReq;
}
else
@@ -640,9 +657,8 @@ const Ptr Ieee80211MacHeaderSerializer::deserializeFields(MemoryInputStre
blockAck->setDurationField(SimTime(stream.readUint16Le(), SIMTIME_US));
blockAck->setReceiverAddress(stream.readMacAddress());
blockAck->setTransmitterAddress(stream.readMacAddress());
- // BA Control is a 16-bit little-endian field: bit 0 Ack Policy, bit 1 Multi-TID,
- // bit 2 Compressed Bitmap, bits 3-11 reserved, bits 12-15 TID_INFO
uint16_t baControl = stream.readUint16Le();
+ auto baType = (baControl >> 1) & 0xF;
bool multiTid = (baControl >> 1) & 1;
bool compressedBitmap = (baControl >> 2) & 1;
uint8_t tidInfo = (baControl >> 12) & 0xF;
@@ -650,7 +666,7 @@ const Ptr Ieee80211MacHeaderSerializer::deserializeFields(MemoryInputStre
blockAck->setMultiTid(multiTid);
blockAck->setCompressedBitmap(compressedBitmap);
blockAck->setReserved((baControl >> 3) & 0x1FF);
- if (!multiTid && !compressedBitmap) {
+ if (baType == 0) {
auto basicBlockAck = makeShared();
copyBasicFields(basicBlockAck, macHeader);
copyBlockAckFrameFields(basicBlockAck, blockAck);
@@ -664,15 +680,16 @@ const Ptr Ieee80211MacHeaderSerializer::deserializeFields(MemoryInputStre
std::vector bytes;
bytes.push_back(stream.readByte());
bytes.push_back(stream.readByte());
- BitVector *blockAckBitmap = new BitVector(bytes);
- basicBlockAck->setBlockAckBitmap(i, *blockAckBitmap);
+ basicBlockAck->setBlockAckBitmap(i, BitVector(bytes));
}
return basicBlockAck;
}
- else if (!multiTid && compressedBitmap) {
+ else if (baType == 2) {
auto compressedBlockAck = makeShared();
copyBasicFields(compressedBlockAck, macHeader);
copyBlockAckFrameFields(compressedBlockAck, blockAck);
+ compressedBlockAck->setBlockAckPolicy(false);
+ compressedBlockAck->setReserved(0);
compressedBlockAck->setTidInfo(tidInfo);
// Block Ack Starting Sequence Control = 4-bit Fragment Number (reserved) + 12-bit
@@ -684,7 +701,9 @@ const Ptr Ieee80211MacHeaderSerializer::deserializeFields(MemoryInputStre
for (size_t i = 0; i < 8; ++i) {
bytes.push_back(stream.readByte());
}
- compressedBlockAck->setBlockAckBitmap(*(new BitVector(bytes)));
+ compressedBlockAck->setBlockAckBitmap(BitVector(bytes));
+ if (fragmentNumber != 0)
+ compressedBlockAck->markIncorrect();
return compressedBlockAck;
}
else {
diff --git a/src/inet/linklayer/ieee80211/mac/Rx.cc b/src/inet/linklayer/ieee80211/mac/Rx.cc
index b51f7f3edf7..caf03f86200 100644
--- a/src/inet/linklayer/ieee80211/mac/Rx.cc
+++ b/src/inet/linklayer/ieee80211/mac/Rx.cc
@@ -12,6 +12,7 @@
#include "inet/linklayer/ieee80211/mac/Ieee80211Mac.h"
#include "inet/linklayer/ieee80211/mac/contract/IContention.h"
#include "inet/linklayer/ieee80211/mac/contract/ITx.h"
+#include "inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211Tag_m.h"
namespace inet {
namespace ieee80211 {
@@ -104,7 +105,10 @@ bool Rx::lowerFrameReceived(Packet *packet)
Enter_Method("lowerFrameReceived(\"%s\")", packet->getName());
take(packet);
- bool isFrameOk = isFcsOk(packet);
+ // Aggregate containers need member acceptance before standalone FCS or header checks.
+ // Reject them until MAC aggregate reception exists; PHY range outcomes stay separate.
+ auto aggregate = packet->findTag();
+ bool isFrameOk = !(aggregate && aggregate->getAggregation()) && isFcsOk(packet);
if (isFrameOk) {
EV_INFO << "Received frame from PHY: " << packet << endl;
const auto& header = packet->peekAtFront();
diff --git a/src/inet/linklayer/ieee80211/mac/aggregation/MpduAggregationUtils.cc b/src/inet/linklayer/ieee80211/mac/aggregation/MpduAggregationUtils.cc
new file mode 100644
index 00000000000..aff3649c878
--- /dev/null
+++ b/src/inet/linklayer/ieee80211/mac/aggregation/MpduAggregationUtils.cc
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "inet/linklayer/ieee80211/mac/aggregation/MpduAggregationUtils.h"
+
+#include "inet/linklayer/ieee80211/mac/Ieee80211Frame_m.h"
+
+namespace inet {
+namespace ieee80211 {
+
+MpduLayout inspectMpduAggregate(const Packet *packet, B offset, B length)
+{
+ MpduLayout layout;
+ if (offset < B(0) || length < B(0) || offset + length > packet->getDataLength()) {
+ layout.complete = false;
+ return layout;
+ }
+ B position(0);
+ // IEEE Std 802.11-2024, 9.7.1: delimiter boundaries are four octets apart after loss of continuity.
+ while (position < length) {
+ if (length - position < B(4)) {
+ layout.complete = false;
+ break;
+ }
+ // Recovery can enter a typed MPDU. Interpret its candidate delimiter bytes explicitly.
+ const auto& delimiter = packet->peekDataAt(offset + position, B(4),
+ Chunk::PF_ALLOW_INCORRECT | Chunk::PF_ALLOW_INCOMPLETE | Chunk::PF_ALLOW_IMPROPERLY_REPRESENTED |
+ Chunk::PF_ALLOW_REINTERPRETATION);
+ bool valid = delimiter->isCorrect() && delimiter->isComplete() && delimiter->isProperlyRepresented() &&
+ delimiter->getLength() >= 0 && delimiter->getLength() <= 4095 &&
+ B(delimiter->getLength()) <= length - position - B(4);
+ layout.ranges.push_back({MpduRangeKind::DELIMITER, position, B(4), valid});
+ position += B(4);
+ if (!valid) {
+ layout.complete = false;
+ continue;
+ }
+ B memberLength(delimiter->getLength());
+ if (memberLength == B(0))
+ continue;
+ layout.ranges.push_back({MpduRangeKind::MPDU, position, memberLength, true});
+ position += memberLength;
+ if (position == length)
+ break;
+ B padding((4 - memberLength.get() % 4) % 4);
+ if (padding > length - position) {
+ layout.complete = false;
+ break;
+ }
+ position += padding;
+ if (position == length)
+ layout.complete = false; // HT has no final alignment padding.
+ }
+ return layout;
+}
+
+} // namespace ieee80211
+} // namespace inet
diff --git a/src/inet/linklayer/ieee80211/mac/aggregation/MpduAggregationUtils.h b/src/inet/linklayer/ieee80211/mac/aggregation/MpduAggregationUtils.h
new file mode 100644
index 00000000000..bff9f954451
--- /dev/null
+++ b/src/inet/linklayer/ieee80211/mac/aggregation/MpduAggregationUtils.h
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#ifndef __INET_MPDUAGGREGATIONUTILS_H
+#define __INET_MPDUAGGREGATIONUTILS_H
+
+#include "inet/common/packet/Packet.h"
+
+namespace inet {
+namespace ieee80211 {
+
+enum class MpduRangeKind { DELIMITER, MPDU };
+
+struct INET_API MpduRange
+{
+ MpduRangeKind kind;
+ B offset;
+ B length;
+ // Delimiter validity includes header flags and member bounds. MPDU validity requires a complete range.
+ bool valid;
+};
+
+struct INET_API MpduLayout
+{
+ std::vector ranges;
+ bool complete = true;
+};
+
+// Offsets are relative to the PSDU, without the PHY header or PHY padding.
+INET_API MpduLayout inspectMpduAggregate(const Packet *packet, B offset, B length);
+
+} // namespace ieee80211
+} // namespace inet
+
+#endif
diff --git a/src/inet/linklayer/ieee80211/mac/aggregation/MpduDeaggregation.cc b/src/inet/linklayer/ieee80211/mac/aggregation/MpduDeaggregation.cc
index 01729897c5d..5dfadcaace3 100644
--- a/src/inet/linklayer/ieee80211/mac/aggregation/MpduDeaggregation.cc
+++ b/src/inet/linklayer/ieee80211/mac/aggregation/MpduDeaggregation.cc
@@ -6,6 +6,7 @@
#include "inet/linklayer/ieee80211/mac/aggregation/MpduDeaggregation.h"
+#include "inet/linklayer/ieee80211/mac/aggregation/MpduAggregationUtils.h"
namespace inet {
namespace ieee80211 {
@@ -16,20 +17,19 @@ std::vector *MpduDeaggregation::deaggregateFrame(Packet *aggregatedFra
{
EV_DEBUG << "Deaggregating A-MPDU " << *aggregatedFrame << " into multiple packets.\n";
std::vector *frames = new std::vector();
- int paddingLength = 0;
+ auto layout = inspectMpduAggregate(aggregatedFrame, B(0), B(aggregatedFrame->getDataLength()));
+ // An incomplete layout can contain bounded recovered members. Extraction does not establish MAC acceptance.
+ // The caller must check delimiter validity, PHY outcomes, member FCS, and MAC conditions before acceptance.
cStringTokenizer tokenizer(aggregatedFrame->getName(), "+");
- while (aggregatedFrame->getDataLength() > b(0)) {
- aggregatedFrame->setFrontOffset(aggregatedFrame->getFrontOffset() + B(paddingLength == 4 ? 0 : paddingLength));
- const auto& mpduSubframeHeader = aggregatedFrame->popAtFront();
- const auto& mpdu = aggregatedFrame->peekDataAt(b(0), B(mpduSubframeHeader->getLength()));
- paddingLength = 4 - (mpduSubframeHeader->getChunkLength() + mpdu->getChunkLength()).get() % 4;
- aggregatedFrame->setFrontOffset(aggregatedFrame->getFrontOffset() + mpdu->getChunkLength());
+ for (const auto& range : layout.ranges) {
+ if (range.kind != MpduRangeKind::MPDU)
+ continue;
+ const auto& mpdu = aggregatedFrame->peekDataAt(range.offset, range.length);
auto frame = new Packet();
- frame->setName(tokenizer.nextToken());
+ frame->setName(tokenizer.hasMoreTokens() ? tokenizer.nextToken() : aggregatedFrame->getName());
frame->insertAtBack(mpdu);
- frame->getRegionTags().copyTags(aggregatedFrame->getRegionTags(), aggregatedFrame->getFrontOffset() - frame->getDataLength(), frame->getFrontOffset(), frame->getDataLength());
+ frame->getRegionTags().copyTags(aggregatedFrame->getRegionTags(), aggregatedFrame->getFrontOffset() + range.offset, frame->getFrontOffset(), range.length);
EV_TRACE << "Created " << *frame << " from A-MPDU.\n";
- // TODO check CRC?
frames->push_back(frame);
}
delete aggregatedFrame;
diff --git a/src/inet/linklayer/ieee80211/mac/blockack/BlockAckRecord.cc b/src/inet/linklayer/ieee80211/mac/blockack/BlockAckRecord.cc
index 919bd813a6f..fa285580703 100644
--- a/src/inet/linklayer/ieee80211/mac/blockack/BlockAckRecord.cc
+++ b/src/inet/linklayer/ieee80211/mac/blockack/BlockAckRecord.cc
@@ -53,6 +53,50 @@ void BlockAckRecord::removeAckStates(SequenceNumberCyclic sequenceNumber)
}
}
+void BlockAckRecord::initializeCompressedWindow(SequenceNumberCyclic start, int size)
+{
+ ASSERT(start.isSpecified() && size > 0 && size <= 64);
+ windowStart = start;
+ windowSize = size;
+ completeReceipts.clear();
+}
+
+void BlockAckRecord::advanceCompressedWindow(SequenceNumberCyclic start)
+{
+ // IEEE Std 802.11-2024, 10.25.6.3: retain explicit receipt truth at WinStartR - 1.
+ if (!(windowStart < start))
+ return;
+ windowStart = start;
+ for (auto it = completeReceipts.begin(); it != completeReceipts.end();) {
+ auto distance = (*it - windowStart.get() + 4096) % 4096;
+ if (distance >= windowSize && distance != 4095)
+ it = completeReceipts.erase(it);
+ else
+ ++it;
+ }
+}
+
+void BlockAckRecord::compressedFrameReceived(const Ptr& header)
+{
+ ASSERT(header->getFragmentNumber() == 0 && !header->getMoreFragments());
+ auto sequence = header->getSequenceNumber();
+ // IEEE Std 802.11-2024, 10.25.6.3: advance before duplicate removal or reorder insertion.
+ if (windowStart <= sequence && !(sequence < windowStart + windowSize))
+ advanceCompressedWindow(sequence - windowSize + 1);
+ if (windowStart <= sequence && sequence < windowStart + windowSize)
+ completeReceipts.insert(sequence.get());
+}
+
+bool BlockAckRecord::hasCompleteReceipt(SequenceNumberCyclic sequenceNumber) const
+{
+ return completeReceipts.count(sequenceNumber.get()) != 0;
+}
+
+bool BlockAckRecord::getCompressedAckState(SequenceNumberCyclic sequenceNumber) const
+{
+ return windowStart <= sequenceNumber && sequenceNumber < windowStart + windowSize && hasCompleteReceipt(sequenceNumber);
+}
+
} /* namespace ieee80211 */
} /* namespace inet */
diff --git a/src/inet/linklayer/ieee80211/mac/blockack/BlockAckRecord.h b/src/inet/linklayer/ieee80211/mac/blockack/BlockAckRecord.h
index 971be8a8af9..1c90bf392dc 100644
--- a/src/inet/linklayer/ieee80211/mac/blockack/BlockAckRecord.h
+++ b/src/inet/linklayer/ieee80211/mac/blockack/BlockAckRecord.h
@@ -25,6 +25,9 @@ class INET_API BlockAckRecord
MacAddress originatorAddress = MacAddress::UNSPECIFIED_ADDRESS;
Tid tid = -1;
std::map acknowledgmentState;
+ SequenceNumberCyclic windowStart;
+ int windowSize = 64; // Sequence positions; the Compressed profile permits 1..64.
+ std::set completeReceipts;
public:
BlockAckRecord(MacAddress originatorAddress, Tid tid);
@@ -33,6 +36,12 @@ class INET_API BlockAckRecord
void blockAckPolicyFrameReceived(const Ptr& header);
bool getAckState(SequenceNumberCyclic sequenceNumber, FragmentNumber fragmentNumber);
void removeAckStates(SequenceNumberCyclic sequenceNumber);
+ void initializeCompressedWindow(SequenceNumberCyclic start, int size);
+ void compressedFrameReceived(const Ptr& header);
+ void advanceCompressedWindow(SequenceNumberCyclic start);
+ bool getCompressedAckState(SequenceNumberCyclic sequenceNumber) const;
+ bool hasCompleteReceipt(SequenceNumberCyclic sequenceNumber) const;
+ SequenceNumberCyclic getWindowStart() const { return windowStart; }
MacAddress getOriginatorAddress() { return originatorAddress; }
Tid getTid() { return tid; }
diff --git a/src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreement.h b/src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreement.h
index a516527bc34..c760205173a 100644
--- a/src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreement.h
+++ b/src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreement.h
@@ -8,6 +8,8 @@
#ifndef __INET_ORIGINATORBLOCKACKAGREEMENT_H
#define __INET_ORIGINATORBLOCKACKAGREEMENT_H
+#include
+
#include "inet/linklayer/ieee80211/mac/common/SequenceControlField.h"
namespace inet {
@@ -24,6 +26,9 @@ class INET_API OriginatorBlockAckAgreement : public cObject
SequenceNumberCyclic startingSequenceNumber;
int bufferSize = -1;
bool isAMsduSupported = false;
+ bool responseAMsduSupported = false;
+ int dialogToken = -1;
+ int64_t highestTransmittedDataIndex = -1;
bool isDelayedBlockAckPolicySupported = false;
bool isAddbaResponseReceived = false;
bool isAddbaRequestSent = false;
@@ -44,11 +49,47 @@ class INET_API OriginatorBlockAckAgreement : public cObject
virtual ~OriginatorBlockAckAgreement() {}
virtual int getBufferSize() const { return bufferSize; }
+ int getWindowSize() const { return std::min(bufferSize, 64); }
+ static int computeSequenceDistance(SequenceNumberCyclic start, SequenceNumberCyclic sequence)
+ {
+ return (sequence.get() - start.get() + 4096) % 4096;
+ }
+ bool containsSequence(SequenceNumberCyclic sequence) const
+ {
+ return computeSequenceDistance(startingSequenceNumber, sequence) < getWindowSize();
+ }
+ // The Basic and Compressed bitmaps cover 64 sequence positions, independent of the accepted window size.
+ static bool isInBitmap(SequenceNumberCyclic sequence, SequenceNumberCyclic bitmapStart)
+ {
+ return computeSequenceDistance(bitmapStart, sequence) < 64;
+ }
+ // Preserve the earliest unresolved position, or advance to the next allocation when none remains.
+ void advanceWindow(SequenceNumberCyclic nextSequence, const std::vector& unresolvedSequences)
+ {
+ auto start = getStartingSequenceNumber();
+ int distance = computeSequenceDistance(start, nextSequence);
+ for (auto sequence : unresolvedSequences)
+ distance = std::min(distance, computeSequenceDistance(start, sequence));
+ setStartingSequenceNumber(start + distance);
+ }
virtual SequenceNumberCyclic getStartingSequenceNumber() { return startingSequenceNumber; }
virtual void setStartingSequenceNumber(SequenceNumberCyclic sequenceNumber) { startingSequenceNumber = sequenceNumber; }
virtual bool getIsAddbaResponseReceived() const { return isAddbaResponseReceived; }
virtual bool getIsAddbaRequestSent() const { return isAddbaRequestSent; }
virtual bool getIsAMsduSupported() const { return isAMsduSupported; }
+ // IEEE Std 802.11-2024, 10.25.2 defines ADDBA negotiation; INET requires both flags before A-MSDU aggregation.
+ bool isAMsduPermitted() const { return isAMsduSupported && responseAMsduSupported; }
+ int getDialogToken() const { return dialogToken; }
+ void setDialogToken(int token) { dialogToken = token; }
+ void setResponseAMsduSupported(bool value) { responseAMsduSupported = value; }
+ bool getFirstDataTransmitted() const { return highestTransmittedDataIndex >= 0; }
+ int64_t getHighestTransmittedDataIndex() const { return highestTransmittedDataIndex; }
+ void recordTransmittedData(int64_t allocationIndex)
+ {
+ ASSERT(allocationIndex >= 0);
+ if (highestTransmittedDataIndex < allocationIndex)
+ highestTransmittedDataIndex = allocationIndex;
+ }
virtual bool getIsDelayedBlockAckPolicySupported() const { return isDelayedBlockAckPolicySupported; }
virtual MacAddress getReceiverAddr() const { return receiverAddr; }
virtual Tid getTid() const { return tid; }
diff --git a/src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreementHandler.cc b/src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreementHandler.cc
index 2f11dc365db..c526b7fe88c 100644
--- a/src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreementHandler.cc
+++ b/src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreementHandler.cc
@@ -8,15 +8,108 @@
#include "inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreementHandler.h"
#include "inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreement.h"
+#include "inet/linklayer/ieee80211/mac/Ieee80211DataSequenceTag_m.h"
namespace inet {
namespace ieee80211 {
+void OriginatorBlockAckAgreementHandler::noteEligibleDataTransmission(MacAddress peer, Tid tid)
+{
+ Procedure procedure;
+ procedure.state = ProcedureState::WAITING_FOR_SUCCESSOR;
+ procedures.emplace(AgreementId(peer, tid), procedure);
+}
+
+void OriginatorBlockAckAgreementHandler::beginSetupIntent(MacAddress peer, Tid tid)
+{
+ auto result = procedures.emplace(AgreementId(peer, tid), Procedure());
+ if (result.first->second.state == ProcedureState::WAITING_FOR_SUCCESSOR)
+ result.first->second.state = ProcedureState::SETUP_INTENT;
+}
+
+void OriginatorBlockAckAgreementHandler::beginTeardown(MacAddress peer, Tid tid)
+{
+ Procedure procedure;
+ procedure.state = ProcedureState::TEARDOWN_PENDING;
+ procedures[{peer, tid}] = procedure;
+}
+
+void OriginatorBlockAckAgreementHandler::failTeardown(MacAddress peer, Tid tid)
+{
+ Procedure procedure;
+ procedure.state = ProcedureState::RECOVERY_NEEDED;
+ procedures[{peer, tid}] = procedure;
+}
+
+void OriginatorBlockAckAgreementHandler::finishProcedure(MacAddress peer, Tid tid)
+{
+ procedures.erase({peer, tid});
+}
+
+void OriginatorBlockAckAgreementHandler::markRequestAttempted(MacAddress peer, Tid tid)
+{
+ auto it = procedures.find({peer, tid});
+ if (it != procedures.end() && it->second.state == ProcedureState::REQUEST_PENDING)
+ it->second.requestAttempted = true;
+}
+
+void OriginatorBlockAckAgreementHandler::acknowledgeRequest(MacAddress peer, Tid tid, simtime_t timeout)
+{
+ auto it = procedures.find({peer, tid});
+ if (it != procedures.end() && it->second.state == ProcedureState::REQUEST_PENDING)
+ it->second.responseDeadline = simTime() + timeout;
+}
+
void OriginatorBlockAckAgreementHandler::createAgreement(const Ptr& addbaRequest)
{
OriginatorBlockAckAgreement *blockAckAgreement = new OriginatorBlockAckAgreement(addbaRequest->getReceiverAddress(), addbaRequest->getTid(), addbaRequest->getStartingSequenceNumber(), addbaRequest->getBufferSize(), addbaRequest->getAMsduSupported(), addbaRequest->getBlockAckPolicy() == 0);
auto agreementId = std::make_pair(addbaRequest->getReceiverAddress(), addbaRequest->getTid());
blockAckAgreements[agreementId] = blockAckAgreement;
+ blockAckAgreement->setDialogToken(addbaRequest->getDialogToken());
+ procedures[agreementId].state = ProcedureState::REQUEST_PENDING;
+}
+
+OriginatorBlockAckAgreement *OriginatorBlockAckAgreementHandler::detachAgreement(MacAddress peer, Tid tid)
+{
+ auto it = blockAckAgreements.find({peer, tid});
+ if (it == blockAckAgreements.end())
+ return nullptr;
+ auto agreement = it->second;
+ blockAckAgreements.erase(it);
+ return agreement;
+}
+
+std::vector OriginatorBlockAckAgreementHandler::detachPeer(MacAddress peer)
+{
+ std::vector removed;
+ for (auto it = blockAckAgreements.begin(); it != blockAckAgreements.end();) {
+ if (it->first.first == peer) {
+ removed.push_back(it->second);
+ it = blockAckAgreements.erase(it);
+ }
+ else
+ ++it;
+ }
+ for (auto it = procedures.begin(); it != procedures.end();) {
+ if (it->first.first == peer)
+ it = procedures.erase(it);
+ else
+ ++it;
+ }
+ return removed;
+}
+
+void OriginatorBlockAckAgreementHandler::startSetup(MacAddress peer, Tid tid, SequenceNumberCyclic sequence,
+ IOriginatorBlockAckAgreementPolicy *policy, IProcedureCallback *callback, bool aMsdu)
+{
+ auto request = buildAddbaRequest(peer, tid, sequence, policy);
+ request->setAMsduSupported(aMsdu && policy->isMsduSupported());
+ // IEEE Std 802.11-2024, Table 9-466: ordinary setup uses a nonzero dialog token.
+ auto& token = dialogTokens[{peer, tid}];
+ token = token % 255 + 1;
+ request->setDialogToken(token);
+ createAgreement(request);
+ callback->processMgmtFrame(new Packet("AddbaReq", request), request);
}
simtime_t OriginatorBlockAckAgreementHandler::computeEarliestExpirationTime()
@@ -40,15 +133,16 @@ void OriginatorBlockAckAgreementHandler::blockAckAgreementExpired(IProcedureCall
// primitive with the ReasonCode parameter having a value of TIMEOUT.
// The procedure is illustrated in Figure 10-14.
simtime_t now = simTime();
+ std::vector expired;
for (auto id : blockAckAgreements) {
auto agreement = id.second;
- if (agreement->getExpirationTime() == now) {
- MacAddress receiverAddr = id.first.first;
- Tid tid = id.first.second;
- const auto& delba = buildDelba(receiverAddr, tid, 39);
- auto delbaPacket = new Packet("Delba", delba);
- procedureCallback->processMgmtFrame(delbaPacket, delba); // 39 - TIMEOUT see: Table 8-36—Reason codes
- }
+ if (agreement->getIsAddbaResponseReceived() && agreement->getExpirationTime() <= now)
+ expired.push_back(id.first);
+ }
+ for (const auto& id : expired) {
+ const auto& delba = buildDelba(id.first, id.second, 39);
+ auto delbaPacket = new Packet("Delba", delba);
+ procedureCallback->processMgmtFrame(delbaPacket, delba);
}
scheduleInactivityTimer(agreementHandlerCallback);
}
@@ -76,7 +170,13 @@ void OriginatorBlockAckAgreementHandler::processReceivedBlockAck(const Ptr(blockAck)) {
auto agreement = getAgreement(basicBlockAck->getTransmitterAddress(), basicBlockAck->getTidInfo());
if (agreement) {
- agreement->setStartingSequenceNumber(basicBlockAck->getStartingSequenceNumber());
+ agreement->calculateExpirationTime();
+ scheduleInactivityTimer(callback);
+ }
+ }
+ else if (auto compressed = dynamicPtrCast(blockAck)) {
+ auto agreement = getAgreement(compressed->getTransmitterAddress(), compressed->getTidInfo());
+ if (agreement) {
agreement->calculateExpirationTime();
scheduleInactivityTimer(callback);
}
@@ -87,9 +187,7 @@ void OriginatorBlockAckAgreementHandler::processReceivedBlockAck(const PtrscheduleInactivityTimer(earliestExpirationTime);
+ callback->scheduleInactivityTimer();
}
OriginatorBlockAckAgreement *OriginatorBlockAckAgreementHandler::getAgreement(MacAddress receiverAddr, Tid tid)
@@ -124,12 +222,8 @@ void OriginatorBlockAckAgreementHandler::terminateAgreement(MacAddress originato
void OriginatorBlockAckAgreementHandler::processTransmittedDataFrame(Packet *packet, const Ptr& dataHeader, IOriginatorBlockAckAgreementPolicy *blockAckAgreementPolicy, IProcedureCallback *callback)
{
auto agreement = getAgreement(dataHeader->getReceiverAddress(), dataHeader->getTid());
- if (blockAckAgreementPolicy->isAddbaReqNeeded(packet, dataHeader) && agreement == nullptr) {
- auto addbaReq = buildAddbaRequest(dataHeader->getReceiverAddress(), dataHeader->getTid(), dataHeader->getSequenceNumber() + 1, blockAckAgreementPolicy);
- createAgreement(addbaReq);
- auto addbaPacket = new Packet("AddbaReq", addbaReq);
- callback->processMgmtFrame(addbaPacket, addbaReq);
- }
+ if (agreement && agreement->getIsAddbaResponseReceived())
+ agreement->recordTransmittedData(packet->getTag()->getAllocationIndex());
}
void OriginatorBlockAckAgreementHandler::processReceivedAddbaResp(const Ptr& addbaResp, IOriginatorBlockAckAgreementPolicy *blockAckAgreementPolicy, IBlockAckAgreementHandlerCallback *callback)
@@ -149,6 +243,7 @@ void OriginatorBlockAckAgreementHandler::updateAgreement(OriginatorBlockAckAgree
agreement->setIsAddbaResponseReceived(true);
agreement->setBufferSize(addbaResp->getBufferSize());
agreement->setBlockAckTimeoutValue(addbaResp->getBlockAckTimeoutValue());
+ agreement->setResponseAMsduSupported(addbaResp->getAMsduSupported());
agreement->calculateExpirationTime();
}
@@ -157,13 +252,11 @@ void OriginatorBlockAckAgreementHandler::processTransmittedAddbaReq(const PtrgetReceiverAddress(), addbaReq->getTid());
if (agreement)
agreement->setIsAddbaRequestSent(true);
- else
- throw cRuntimeError("Block Ack Agreement should have already been added");
}
void OriginatorBlockAckAgreementHandler::processTransmittedDelba(const Ptr& delba)
{
- terminateAgreement(delba->getReceiverAddress(), delba->getTid());
+ // HCF retires the agreement before it queues DELBA. ACK completes teardown.
}
void OriginatorBlockAckAgreementHandler::processReceivedDelba(const Ptr& delba, IOriginatorBlockAckAgreementPolicy *blockAckAgreementPolicy)
diff --git a/src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreementHandler.h b/src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreementHandler.h
index 4a449d17da3..af0c99d0cb7 100644
--- a/src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreementHandler.h
+++ b/src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreementHandler.h
@@ -21,6 +21,8 @@ class INET_API OriginatorBlockAckAgreementHandler : public IOriginatorBlockAckAg
{
protected:
std::map, OriginatorBlockAckAgreement *> blockAckAgreements;
+ std::map procedures;
+ std::map dialogTokens;
protected:
virtual const Ptr buildAddbaRequest(MacAddress receiverAddr, Tid tid, SequenceNumberCyclic startingSequenceNumber, IOriginatorBlockAckAgreementPolicy *blockAckAgreementPolicy);
@@ -28,10 +30,22 @@ class INET_API OriginatorBlockAckAgreementHandler : public IOriginatorBlockAckAg
virtual void updateAgreement(OriginatorBlockAckAgreement *agreement, const Ptr& addbaResp);
virtual void terminateAgreement(MacAddress originatorAddr, Tid tid);
virtual const Ptr buildDelba(MacAddress receiverAddr, Tid tid, int reasonCode);
- virtual simtime_t computeEarliestExpirationTime();
virtual void scheduleInactivityTimer(IBlockAckAgreementHandlerCallback *callback);
public:
+ virtual simtime_t computeEarliestExpirationTime() override;
+ const std::map& getAgreements() const override { return blockAckAgreements; }
+ const std::map& getProcedures() const override { return procedures; }
+ virtual void noteEligibleDataTransmission(MacAddress peer, Tid tid) override;
+ virtual void beginSetupIntent(MacAddress peer, Tid tid) override;
+ virtual void beginTeardown(MacAddress peer, Tid tid) override;
+ virtual void failTeardown(MacAddress peer, Tid tid) override;
+ virtual void finishProcedure(MacAddress peer, Tid tid) override;
+ virtual void markRequestAttempted(MacAddress peer, Tid tid) override;
+ virtual void acknowledgeRequest(MacAddress peer, Tid tid, simtime_t timeout) override;
+ OriginatorBlockAckAgreement *detachAgreement(MacAddress peer, Tid tid) override;
+ std::vector detachPeer(MacAddress peer) override;
+ void startSetup(MacAddress peer, Tid tid, SequenceNumberCyclic sequence, IOriginatorBlockAckAgreementPolicy *policy, IProcedureCallback *callback, bool aMsdu) override;
virtual ~OriginatorBlockAckAgreementHandler();
virtual void processTransmittedAddbaReq(const Ptr& addbaReq) override;
virtual void processTransmittedDataFrame(Packet *packet, const Ptr& dataHeader, IOriginatorBlockAckAgreementPolicy *blockAckAgreementPolicy, IProcedureCallback *callback) override;
diff --git a/src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreementPolicy.cc b/src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreementPolicy.cc
index 44b4c0874fa..1a03dd26ef2 100644
--- a/src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreementPolicy.cc
+++ b/src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreementPolicy.cc
@@ -9,6 +9,7 @@
#include "inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreement.h"
#include "inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.h"
+#include "inet/linklayer/ieee80211/mgmt/Ieee80211MgmtFrame_m.h"
namespace inet {
namespace ieee80211 {
@@ -24,15 +25,25 @@ void OriginatorBlockAckAgreementPolicy::initialize(int stage)
aMsduSupported = par("aMsduSupported");
maximumAllowedBufferSize = par("maximumAllowedBufferSize");
blockAckTimeoutValue = par("blockAckTimeoutValue");
- // TODO addbaFailureTimeout = par("addbaFailureTimeout");
+ addbaFailureTimeout = par("addbaFailureTimeout");
+ if (addbaFailureTimeout <= SIMTIME_ZERO || addbaFailureTimeout == SIMTIME_MAX)
+ throw cRuntimeError("addbaFailureTimeout must be positive and finite");
+ if (maximumAllowedBufferSize <= 0 || blockAckTimeoutValue < SIMTIME_ZERO)
+ throw cRuntimeError("Invalid Block Ack buffer size or inactivity timeout");
WATCH(blockAckReqThreshold);
}
}
simtime_t OriginatorBlockAckAgreementPolicy::computeAddbaFailureTimeout() const
{
- // TODO ADDBAFailureTimeout -- 6.3.29.2.2 Semantics of the service primitive
- throw cRuntimeError("Unimplemented");
+ return addbaFailureTimeout;
+}
+
+bool OriginatorBlockAckAgreementPolicy::isProfileAllowed(BlockAckVariant variant, bool localHt, bool peerHt) const
+{
+ if (variant == BlockAckVariant::COMPRESSED && (!localHt || !peerHt))
+ EV_DEBUG << "Compressed Block Ack setup refused: " << (!localHt ? "local HT state is absent" : "peer HT state is absent") << "; DATA uses Normal Ack.\n";
+ return variant == BlockAckVariant::BASIC || (localHt && peerHt);
}
bool OriginatorBlockAckAgreementPolicy::isAddbaReqNeeded(Packet *packet, const Ptr& header)
@@ -42,8 +53,16 @@ bool OriginatorBlockAckAgreementPolicy::isAddbaReqNeeded(Packet *packet, const P
bool OriginatorBlockAckAgreementPolicy::isAddbaReqAccepted(const Ptr& addbaResp, OriginatorBlockAckAgreement *agreement)
{
- ASSERT(agreement);
- return true;
+ // IEEE Std 802.11-2024, 10.25.2 and Table 9-466: match the pending setup and its nonzero dialog token.
+ // Immediate Block Ack and the 64-entry bound are INET profile limits for historical Basic compatibility.
+ return agreement != nullptr && !agreement->getIsAddbaResponseReceived() &&
+ addbaResp->getTransmitterAddress() == agreement->getReceiverAddr() &&
+ addbaResp->getTid() == agreement->getTid() &&
+ addbaResp->getDialogToken() > 0 && addbaResp->getDialogToken() <= 255 &&
+ addbaResp->getDialogToken() == agreement->getDialogToken() &&
+ addbaResp->getStatusCode() == SC_SUCCESSFUL && addbaResp->getBlockAckPolicy() &&
+ addbaResp->getBufferSize() > 0 && addbaResp->getBufferSize() <= 64 &&
+ addbaResp->getBlockAckTimeoutValue() >= SIMTIME_ZERO;
}
bool OriginatorBlockAckAgreementPolicy::isDelbaAccepted(const Ptr& delba)
diff --git a/src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreementPolicy.h b/src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreementPolicy.h
index b762cc29828..b0b7e9cce24 100644
--- a/src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreementPolicy.h
+++ b/src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreementPolicy.h
@@ -32,6 +32,7 @@ class INET_API OriginatorBlockAckAgreementPolicy : public ModeSetListener, publi
virtual void initialize(int stage) override;
public:
+ virtual bool isProfileAllowed(BlockAckVariant variant, bool localHt, bool peerHt) const override;
virtual bool isAddbaReqNeeded(Packet *packet, const Ptr& header) override;
virtual bool isAddbaReqAccepted(const Ptr& addbaResp, OriginatorBlockAckAgreement *agreement) override;
virtual bool isDelbaAccepted(const Ptr& delba) override;
diff --git a/src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreementPolicy.ned b/src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreementPolicy.ned
index bbb569b4b20..ab9970f883f 100644
--- a/src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreementPolicy.ned
+++ b/src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreementPolicy.ned
@@ -21,8 +21,9 @@ simple OriginatorBlockAckAgreementPolicy extends SimpleModule like IOriginatorBl
bool delayedAckPolicySupported = default(false);
bool aMsduSupported = default(true);
- int maximumAllowedBufferSize = default(64);
- double blockAckTimeoutValue @unit(s) = default(0s); // 0 means that it depends on the originator
+ int maximumAllowedBufferSize = default(64); // The ADDBA Request uses this advisory buffer size; the recipient response determines the accepted allocation.
+ double blockAckTimeoutValue @unit(s) = default(0s); // Zero disables inactivity expiration.
+ double addbaFailureTimeout @unit(s) = default(100ms); // Response wait after request ACK.
@display("i=block/control");
}
diff --git a/src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckProcedure.cc b/src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckProcedure.cc
index d02e412b644..ee1960c3fb0 100644
--- a/src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckProcedure.cc
+++ b/src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckProcedure.cc
@@ -12,13 +12,11 @@ namespace ieee80211 {
const Ptr OriginatorBlockAckProcedure::buildCompressedBlockAckReqFrame(const MacAddress& receiverAddress, Tid tid, SequenceNumberCyclic startingSequenceNumber) const
{
- throw cRuntimeError("Unsupported feature");
- // TODO implement
- // auto blockAckReq = makeShared();
- // blockAckReq->setReceiverAddress(receiverAddress);
- // blockAckReq->setStartingSequenceNumber(startingSequenceNumber);
- // blockAckReq->setTidInfo(tid);
- // return blockAckReq;
+ auto blockAckReq = makeShared();
+ blockAckReq->setReceiverAddress(receiverAddress);
+ blockAckReq->setStartingSequenceNumber(startingSequenceNumber);
+ blockAckReq->setTidInfo(tid);
+ return blockAckReq;
}
const Ptr OriginatorBlockAckProcedure::buildBasicBlockAckReqFrame(const MacAddress& receiverAddress, Tid tid, SequenceNumberCyclic startingSequenceNumber) const
diff --git a/src/inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckAgreement.h b/src/inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckAgreement.h
index 40a4186fead..db10d2d489b 100644
--- a/src/inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckAgreement.h
+++ b/src/inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckAgreement.h
@@ -9,6 +9,7 @@
#define __INET_RECIPIENTBLOCKACKAGREEMENT_H
#include "inet/linklayer/ieee80211/mac/blockack/BlockAckRecord.h"
+#include "inet/linklayer/ieee80211/mac/contract/BlockAckVariant.h"
namespace inet {
namespace ieee80211 {
@@ -23,6 +24,9 @@ class INET_API RecipientBlockAckAgreement : public cObject
simtime_t blockAckTimeoutValue = 0;
bool isAddbaResponseSent = false;
simtime_t expirationTime = -1;
+ SequenceNumberCyclic highestReceivedSequence;
+ int highestReceivedFragment = 0;
+ BlockAckVariant variant = BlockAckVariant::BASIC;
public:
RecipientBlockAckAgreement(MacAddress originatorAddress, Tid tid, SequenceNumberCyclic startingSequenceNumber, int bufferSize, simtime_t blockAckTimeoutValue);
@@ -33,6 +37,23 @@ class INET_API RecipientBlockAckAgreement : public cObject
virtual BlockAckRecord *getBlockAckRecord() const { return blockAckRecord; }
virtual simtime_t getBlockAckTimeoutValue() const { return blockAckTimeoutValue; }
virtual int getBufferSize() const { return bufferSize; }
+ BlockAckVariant getVariant() const { return variant; }
+ void setVariant(BlockAckVariant value) {
+ variant = value;
+ if (variant == BlockAckVariant::COMPRESSED)
+ blockAckRecord->initializeCompressedWindow(startingSequenceNumber, bufferSize);
+ }
+ void recordReceived(const Ptr& header) {
+ auto sequence = header->getSequenceNumber();
+ if (!highestReceivedSequence.isSpecified() || highestReceivedSequence < sequence) {
+ highestReceivedSequence = sequence;
+ highestReceivedFragment = header->getFragmentNumber();
+ }
+ else if (highestReceivedSequence == sequence)
+ highestReceivedFragment = std::max(highestReceivedFragment, int(header->getFragmentNumber()));
+ }
+ const SequenceNumberCyclic& getHighestReceivedSequence() const { return highestReceivedSequence; }
+ int getHighestReceivedFragment() const { return highestReceivedFragment; }
virtual SequenceNumberCyclic getStartingSequenceNumber() const { return startingSequenceNumber; }
virtual void addbaResposneSent() { isAddbaResponseSent = true; }
diff --git a/src/inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckAgreementHandler.cc b/src/inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckAgreementHandler.cc
index 6219ac77140..717368a5a46 100644
--- a/src/inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckAgreementHandler.cc
+++ b/src/inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckAgreementHandler.cc
@@ -8,6 +8,7 @@
#include "inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckAgreementHandler.h"
#include "inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckAgreement.h"
+#include "inet/linklayer/ieee80211/mgmt/Ieee80211MgmtFrame_m.h"
namespace inet {
namespace ieee80211 {
@@ -24,9 +25,7 @@ simtime_t RecipientBlockAckAgreementHandler::computeEarliestExpirationTime()
void RecipientBlockAckAgreementHandler::scheduleInactivityTimer(IBlockAckAgreementHandlerCallback *callback)
{
- simtime_t earliestExpirationTime = computeEarliestExpirationTime();
- if (earliestExpirationTime != SIMTIME_MAX)
- callback->scheduleInactivityTimer(earliestExpirationTime);
+ callback->scheduleInactivityTimer();
}
// The inactivity timer at a recipient is reset when MPDUs corresponding to the TID for which the Block Ack
@@ -35,12 +34,15 @@ void RecipientBlockAckAgreementHandler::scheduleInactivityTimer(IBlockAckAgreeme
//
void RecipientBlockAckAgreementHandler::qosFrameReceived(const Ptr& qosHeader, IBlockAckAgreementHandlerCallback *callback)
{
+ // IEEE Std 802.11-2024, 11.5.4: matching Block Ack-policy DATA refreshes inactivity, including a duplicate.
if (qosHeader->getAckPolicy() == AckPolicy::BLOCK_ACK) { // TODO + Implicit Block Ack
Tid tid = qosHeader->getTid();
MacAddress originatorAddr = qosHeader->getTransmitterAddress();
auto agreement = getAgreement(tid, originatorAddr);
- if (agreement)
+ if (agreement) {
+ agreement->calculateExpirationTime();
scheduleInactivityTimer(callback);
+ }
}
}
@@ -51,15 +53,16 @@ void RecipientBlockAckAgreementHandler::blockAckAgreementExpired(IProcedureCallb
// primitive with the ReasonCode parameter having a value of TIMEOUT.
// The procedure is illustrated in Figure 10-14.
simtime_t now = simTime();
+ std::vector> expired;
for (auto id : blockAckAgreements) {
auto agreement = id.second;
- if (agreement->getExpirationTime() == now) {
- MacAddress receiverAddr = id.first.first;
- Tid tid = id.first.second;
- const auto& delba = buildDelba(receiverAddr, tid, 39);
- auto delbaPacket = new Packet("Delba", delba);
- procedureCallback->processMgmtFrame(delbaPacket, delba); // 39 - TIMEOUT see: Table 8-36—Reason codes
- }
+ if (agreement->getExpirationTime() <= now)
+ expired.push_back(id.first);
+ }
+ for (const auto& id : expired) {
+ const auto& delba = buildDelba(id.first, id.second, 39);
+ auto delbaPacket = new Packet("Delba", delba);
+ procedureCallback->processMgmtFrame(delbaPacket, delba);
}
scheduleInactivityTimer(agreementHandlerCallback);
}
@@ -110,8 +113,10 @@ const Ptr RecipientBlockAckAgreementHandler::buildAddbaR
Tid tid = addbaRequest->getTid();
addbaResponse->setTid(tid);
addbaResponse->setBlockAckPolicy(!addbaRequest->getBlockAckPolicy() && blockAckAgreementPolicy->delayedBlockAckPolicySupported() ? false : true);
- addbaResponse->setBufferSize(addbaRequest->getBufferSize() <= blockAckAgreementPolicy->getMaximumAllowedBufferSize() ? addbaRequest->getBufferSize() : blockAckAgreementPolicy->getMaximumAllowedBufferSize());
- addbaResponse->setBlockAckTimeoutValue(blockAckAgreementPolicy->getBlockAckTimeoutValue() == 0 ? blockAckAgreementPolicy->getBlockAckTimeoutValue() : addbaRequest->getBlockAckTimeoutValue());
+ addbaResponse->setDialogToken(addbaRequest->getDialogToken());
+ // IEEE Std 802.11-2024, 10.25.2: the request allocation and timeout are advisory.
+ addbaResponse->setBufferSize(std::min(64, blockAckAgreementPolicy->getMaximumAllowedBufferSize()));
+ addbaResponse->setBlockAckTimeoutValue(blockAckAgreementPolicy->getBlockAckTimeoutValue());
addbaResponse->setAMsduSupported(blockAckAgreementPolicy->aMsduSupported());
return addbaResponse;
}
@@ -124,8 +129,6 @@ void RecipientBlockAckAgreementHandler::updateAgreement(const Ptrsecond;
agreement->addbaResposneSent();
}
- else
- throw cRuntimeError("Agreement is not found");
}
void RecipientBlockAckAgreementHandler::terminateAgreement(MacAddress originatorAddr, Tid tid)
@@ -155,26 +158,62 @@ void RecipientBlockAckAgreementHandler::processTransmittedAddbaResp(const Ptr& addbaRequest, IRecipientBlockAckAgreementPolicy *blockAckAgreementPolicy, IProcedureCallback *callback)
{
EV_INFO << "Processing Addba Request from " << addbaRequest->getTransmitterAddress() << endl;
- if (blockAckAgreementPolicy->isAddbaReqAccepted(addbaRequest)) {
+ auto id = std::make_pair(addbaRequest->getTransmitterAddress(), addbaRequest->getTid());
+ auto addbaResponse = buildAddbaResponse(addbaRequest, blockAckAgreementPolicy);
+ bool accepted = blockAckAgreementPolicy->isAddbaReqAccepted(addbaRequest) &&
+ blockAckAgreements.count(id) == 0 && pendingTeardowns.count(id) == 0;
+ addbaResponse->setStatusCode(accepted ? SC_SUCCESSFUL : SC_UNSPECIFIED);
+ if (accepted) {
EV_DETAIL << "Addba Request has been accepted. Creating a new Block Ack Agreement." << endl;
- auto agreement = addAgreement(addbaRequest);
+ auto parameters = makeShared(*addbaRequest);
+ parameters->setBufferSize(addbaResponse->getBufferSize());
+ parameters->setBlockAckTimeoutValue(addbaResponse->getBlockAckTimeoutValue());
+ auto agreement = addAgreement(parameters);
EV_DETAIL << "Agreement is added with the following parameters: " << *agreement << endl;
EV_DETAIL << "Building Addba Response" << endl;
- auto addbaResponse = buildAddbaResponse(addbaRequest, blockAckAgreementPolicy);
- auto addbaResponsePacket = new Packet("AddbaResponse", addbaResponse);
- callback->processMgmtFrame(addbaResponsePacket, addbaResponse);
}
+ callback->processMgmtFrame(new Packet("AddbaResponse", addbaResponse), addbaResponse);
+}
+
+RecipientBlockAckAgreement *RecipientBlockAckAgreementHandler::detachAgreement(MacAddress peer, Tid tid)
+{
+ auto it = blockAckAgreements.find({peer, tid});
+ if (it == blockAckAgreements.end())
+ return nullptr;
+ auto agreement = it->second;
+ blockAckAgreements.erase(it);
+ return agreement;
+}
+
+std::vector RecipientBlockAckAgreementHandler::detachPeer(MacAddress peer)
+{
+ std::vector removed;
+ for (auto it = blockAckAgreements.begin(); it != blockAckAgreements.end();) {
+ if (it->first.first == peer) {
+ removed.push_back(it->second);
+ it = blockAckAgreements.erase(it);
+ }
+ else
+ ++it;
+ }
+ for (auto it = pendingTeardowns.begin(); it != pendingTeardowns.end();) {
+ if (it->first == peer)
+ it = pendingTeardowns.erase(it);
+ else
+ ++it;
+ }
+ return removed;
}
void RecipientBlockAckAgreementHandler::processTransmittedDelba(const Ptr& delba)
{
- terminateAgreement(delba->getReceiverAddress(), delba->getTid());
+ // HCF retains teardown state until the Action reaches a terminal result.
}
void RecipientBlockAckAgreementHandler::processReceivedDelba(const Ptr& delba, IRecipientBlockAckAgreementPolicy *blockAckAgreementPolicy)
{
if (blockAckAgreementPolicy->isDelbaAccepted(delba))
- terminateAgreement(delba->getReceiverAddress(), delba->getTid());
+ terminateAgreement(delba->getTransmitterAddress(), delba->getTid());
}
RecipientBlockAckAgreementHandler::~RecipientBlockAckAgreementHandler()
diff --git a/src/inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckAgreementHandler.h b/src/inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckAgreementHandler.h
index 1e65a1557ef..ac93c8366e6 100644
--- a/src/inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckAgreementHandler.h
+++ b/src/inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckAgreementHandler.h
@@ -26,6 +26,7 @@ class INET_API RecipientBlockAckAgreementHandler : public IRecipientBlockAckAgre
{
protected:
std::map, RecipientBlockAckAgreement *> blockAckAgreements;
+ std::set> pendingTeardowns;
protected:
virtual void terminateAgreement(MacAddress originatorAddr, Tid tid);
@@ -33,10 +34,16 @@ class INET_API RecipientBlockAckAgreementHandler : public IRecipientBlockAckAgre
virtual void updateAgreement(const Ptr& addbaResponse);
virtual const Ptr buildAddbaResponse(const Ptr& addbaRequest, IRecipientBlockAckAgreementPolicy *blockAckAgreementPolicy);
virtual const Ptr buildDelba(MacAddress receiverAddr, Tid tid, int reasonCode);
- virtual simtime_t computeEarliestExpirationTime();
virtual void scheduleInactivityTimer(IBlockAckAgreementHandlerCallback *callback);
public:
+ virtual simtime_t computeEarliestExpirationTime() override;
+ const std::map, RecipientBlockAckAgreement *>& getAgreements() const override { return blockAckAgreements; }
+ bool isTeardownPending(MacAddress peer, Tid tid) const override { return pendingTeardowns.count({peer, tid}) != 0; }
+ void beginTeardown(MacAddress peer, Tid tid) override { pendingTeardowns.insert({peer, tid}); }
+ void finishTeardown(MacAddress peer, Tid tid) override { pendingTeardowns.erase({peer, tid}); }
+ RecipientBlockAckAgreement *detachAgreement(MacAddress peer, Tid tid) override;
+ std::vector detachPeer(MacAddress peer) override;
virtual ~RecipientBlockAckAgreementHandler();
virtual void processTransmittedAddbaResp(const Ptr& addbaResp, IBlockAckAgreementHandlerCallback *callback) override;
virtual void processReceivedAddbaRequest(const Ptr& addbaRequest, IRecipientBlockAckAgreementPolicy *blockAckAgreementPolicy, IProcedureCallback *callback) override;
diff --git a/src/inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckAgreementPolicy.cc b/src/inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckAgreementPolicy.cc
index 73813feaa5d..33360fcfc32 100644
--- a/src/inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckAgreementPolicy.cc
+++ b/src/inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckAgreementPolicy.cc
@@ -19,12 +19,29 @@ void RecipientBlockAckAgreementPolicy::initialize(int stage)
isAMsduSupported = par("aMsduSupported");
maximumAllowedBufferSize = par("maximumAllowedBufferSize");
blockAckTimeoutValue = par("blockAckTimeoutValue");
+ if (maximumAllowedBufferSize <= 0 || blockAckTimeoutValue < SIMTIME_ZERO)
+ throw cRuntimeError("Invalid Block Ack buffer size or inactivity timeout");
}
}
bool RecipientBlockAckAgreementPolicy::isAddbaReqAccepted(const Ptr& addbaReq)
{
- return true;
+ // IEEE Std 802.11-2024, 10.25.2 and Table 9-466: validate ordinary setup; the requested buffer size is advisory.
+ // INET supports immediate Block Ack for historical Basic compatibility and selects at most 64 entries in the response.
+ return !addbaReq->getTransmitterAddress().isUnspecified() &&
+ !addbaReq->getTransmitterAddress().isMulticast() &&
+ addbaReq->getTid() >= 0 && addbaReq->getTid() <= 15 &&
+ addbaReq->getDialogToken() > 0 && addbaReq->getDialogToken() <= 255 &&
+ addbaReq->getBlockAckPolicy() && addbaReq->getBufferSize() >= 0 &&
+ addbaReq->getBufferSize() <= 1023 && addbaReq->getStartingSequenceNumber().isSpecified() &&
+ addbaReq->getBlockAckTimeoutValue() >= SIMTIME_ZERO;
+}
+
+bool RecipientBlockAckAgreementPolicy::isProfileAllowed(BlockAckVariant variant, bool localHt, bool peerHt) const
+{
+ if (variant == BlockAckVariant::COMPRESSED && (!localHt || !peerHt))
+ EV_DEBUG << "Compressed Block Ack setup refused: " << (!localHt ? "local HT state is absent" : "peer HT state is absent") << "; DATA uses Normal Ack.\n";
+ return variant == BlockAckVariant::BASIC || (localHt && peerHt);
}
bool RecipientBlockAckAgreementPolicy::isDelbaAccepted(const Ptr& delba)
diff --git a/src/inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckAgreementPolicy.h b/src/inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckAgreementPolicy.h
index 995fcc29fdb..d74bfd4240b 100644
--- a/src/inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckAgreementPolicy.h
+++ b/src/inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckAgreementPolicy.h
@@ -28,6 +28,7 @@ class INET_API RecipientBlockAckAgreementPolicy : public SimpleModule, public IR
virtual void initialize(int stage) override;
public:
+ virtual bool isProfileAllowed(BlockAckVariant variant, bool localHt, bool peerHt) const override;
virtual bool isAddbaReqAccepted(const Ptr& addbaReq) override;
virtual bool isDelbaAccepted(const Ptr& delba) override;
diff --git a/src/inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckAgreementPolicy.ned b/src/inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckAgreementPolicy.ned
index 8c41285b009..6fb403594cf 100644
--- a/src/inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckAgreementPolicy.ned
+++ b/src/inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckAgreementPolicy.ned
@@ -20,7 +20,7 @@ simple RecipientBlockAckAgreementPolicy extends SimpleModule like IRecipientBloc
bool delayedAckPolicySupported = default(false);
bool aMsduSupported = default(true);
int maximumAllowedBufferSize = default(64);
- double blockAckTimeoutValue @unit(s) = default(0s); // 0 means that it depends on the originator
+ double blockAckTimeoutValue @unit(s) = default(0s); // Zero disables inactivity expiration.
@display("i=block/control");
}
diff --git a/src/inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckProcedure.cc b/src/inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckProcedure.cc
index ab7cb050e5f..61d1213020c 100644
--- a/src/inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckProcedure.cc
+++ b/src/inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckProcedure.cc
@@ -31,6 +31,14 @@ void RecipientBlockAckProcedure::processReceivedBlockAckReq(Packet *blockAckPack
callback->transmitControlResponseFrame(blockAckPacket, blockAck, blockAckPacketReq, basicBlockAckReq);
}
}
+ else if (auto compressed = dynamicPtrCast(blockAckReq)) {
+ auto agreement = blockAckAgreementHandler->getAgreement(compressed->getTidInfo(), compressed->getTransmitterAddress());
+ if (ackPolicy->isBlockAckNeeded(compressed, agreement)) {
+ auto blockAck = buildBlockAck(compressed, agreement);
+ blockAck->setDurationField(ackPolicy->computeCompressedBlockAckDurationField(blockAckPacketReq, compressed));
+ callback->transmitControlResponseFrame(new Packet("CompressedBlockAck", blockAck), blockAck, blockAckPacketReq, compressed);
+ }
+ }
else
throw cRuntimeError("Unsupported BlockAckReq");
}
@@ -65,6 +73,20 @@ const Ptr RecipientBlockAckProcedure::buildBlockAck(const Ptr
blockAck->setTidInfo(basicBlockAckReq->getTidInfo());
return blockAck;
}
+ else if (auto compressed = dynamicPtrCast(blockAckReq)) {
+ ASSERT(agreement != nullptr);
+ auto blockAck = makeShared();
+ auto record = agreement->getBlockAckRecord();
+ auto start = record->getWindowStart();
+ BitVector bitmap(0, 64);
+ for (int i = 0; i < 64; i++)
+ bitmap.setBit(i, record->getCompressedAckState(start + i));
+ blockAck->setBlockAckBitmap(bitmap);
+ blockAck->setReceiverAddress(compressed->getTransmitterAddress());
+ blockAck->setStartingSequenceNumber(start);
+ blockAck->setTidInfo(compressed->getTidInfo());
+ return blockAck;
+ }
else
throw cRuntimeError("Unsupported Block Ack Request");
}
diff --git a/src/inet/linklayer/ieee80211/mac/blockackreordering/BlockAckReordering.cc b/src/inet/linklayer/ieee80211/mac/blockackreordering/BlockAckReordering.cc
index 6cffea3c37d..78f06a2d725 100644
--- a/src/inet/linklayer/ieee80211/mac/blockackreordering/BlockAckReordering.cc
+++ b/src/inet/linklayer/ieee80211/mac/blockackreordering/BlockAckReordering.cc
@@ -12,11 +12,50 @@
namespace inet {
namespace ieee80211 {
-BlockAckReordering::ReleasedFrames BlockAckReordering::processReceivedQoSFrame(RecipientBlockAckAgreement *agreement, Packet *dataPacket, const Ptr& dataHeader)
+BlockAckReordering::ReleasedFrames BlockAckReordering::detachAgreement(MacAddress peer, Tid tid,
+ bool deliverComplete, std::vector& discarded)
+{
+ auto it = receiveBuffers.find({tid, peer});
+ if (it == receiveBuffers.end())
+ return {};
+ auto buffer = it->second;
+ receiveBuffers.erase(it);
+ ReleasedFrames released;
+ auto start = buffer->getNextExpectedSequenceNumber();
+ for (int i = 0; i < buffer->getBufferSize(); i++) {
+ auto sequence = start + i;
+ auto position = buffer->getBuffer().find(sequence.get());
+ if (position == buffer->getBuffer().end())
+ continue;
+ auto fragments = position->second;
+ if (deliverComplete && isComplete(fragments))
+ released.push_back({sequence.get(), fragments});
+ else
+ discarded.insert(discarded.end(), fragments.begin(), fragments.end());
+ buffer->removeFrame(sequence);
+ }
+ delete buffer;
+ return released;
+}
+
+void BlockAckReordering::clearPeer(MacAddress peer)
+{
+ for (auto it = receiveBuffers.begin(); it != receiveBuffers.end();) {
+ if (it->first.second == peer) {
+ delete it->second;
+ it = receiveBuffers.erase(it);
+ }
+ else
+ ++it;
+ }
+}
+
+BlockAckReordering::ReleasedFrames BlockAckReordering::processReceivedQoSFrame(RecipientBlockAckAgreement *agreement, Packet *dataPacket, const Ptr& dataHeader, bool interframe)
{
ReceiveBuffer *receiveBuffer = createReceiveBufferIfNecessary(agreement);
auto sequenceNumber = dataHeader->getSequenceNumber();
auto windowStart = receiveBuffer->getNextExpectedSequenceNumber();
+ bool insideWindow = windowStart <= sequenceNumber && sequenceNumber < windowStart + receiveBuffer->getBufferSize();
ReleasedFrames releasedFrames;
// IEEE Std 802.11-2024, 10.25.6.6.2.1(b,c): advance for newer DATA beyond the window; discard older DATA.
// The shared reorder service also retains Basic fragment reassembly as an INET compatibility choice.
@@ -31,8 +70,15 @@ BlockAckReordering::ReleasedFrames BlockAckReordering::processReceivedQoSFrame(R
// This allows the recipient to delete the Block Ack if the originator does not switch
// back to using Block Ack.
if (receiveBuffer->insertFrame(dataPacket, dataHeader)) {
- if (dataHeader->getAckPolicy() == BLOCK_ACK)
+ if (dataHeader->getAckPolicy() == BLOCK_ACK && agreement->getVariant() == BlockAckVariant::BASIC)
agreement->blockAckPolicyFrameReceived(dataHeader);
+ auto record = agreement->getBlockAckRecord();
+ // IEEE Std 802.11-2024, 10.25.6.6.2.1(a)(2)(v) and 10.25.6.6.1: release the complete prefix in order.
+ if (agreement->getVariant() == BlockAckVariant::COMPRESSED && insideWindow && windowStart < sequenceNumber &&
+ interframe && record->hasCompleteReceipt(record->getWindowStart() - 1)) {
+ auto preceding = advanceReceiveBuffer(agreement, receiveBuffer, sequenceNumber);
+ releasedFrames.insert(releasedFrames.end(), preceding.begin(), preceding.end());
+ }
// IEEE Std 802.11-2024, 10.25.6.6.2.1(a)(2): release every consecutive complete unit at the window start.
auto consecutiveFrames = collectConsecutiveCompleteFollowingMpdus(receiveBuffer, receiveBuffer->getNextExpectedSequenceNumber());
releaseReceiveBuffer(agreement, receiveBuffer, consecutiveFrames);
@@ -54,6 +100,14 @@ BlockAckReordering::ReleasedFrames BlockAckReordering::processReceivedBlockAckRe
return ReleasedFrames();
startingSequenceNumber = basicReq->getStartingSequenceNumber();
}
+ else if (auto compressedReq = dynamicPtrCast(blockAckReq)) {
+ if (compressedReq->getFragmentNumber() != 0 || compressedReq->getMultiTid() || !compressedReq->getCompressedBitmap() ||
+ compressedReq->getTidInfo() < 0 || compressedReq->getTidInfo() > 15 || !compressedReq->getStartingSequenceNumber().isSpecified() ||
+ compressedReq->getChunkLength() != LENGTH_BLOCKACKREQ - B(4))
+ return ReleasedFrames();
+ startingSequenceNumber = compressedReq->getStartingSequenceNumber();
+ agreement->getBlockAckRecord()->advanceCompressedWindow(startingSequenceNumber);
+ }
else {
throw cRuntimeError("Multi-Tid BlockAckReq is currently an unimplemented feature");
}
@@ -171,7 +225,8 @@ void BlockAckReordering::passedUp(RecipientBlockAckAgreement *agreement, Receive
receiveBuffer->setNextExpectedSequenceNumber(sequenceNumber + 1);
receiveBuffer->dropFramesUntil(sequenceNumber);
receiveBuffer->removeFrame(sequenceNumber);
- agreement->getBlockAckRecord()->removeAckStates(sequenceNumber);
+ if (agreement->getVariant() == BlockAckVariant::BASIC)
+ agreement->getBlockAckRecord()->removeAckStates(sequenceNumber);
}
std::vector BlockAckReordering::getEarliestCompleteMsduOrAMsduIfExists(ReceiveBuffer *receiveBuffer)
diff --git a/src/inet/linklayer/ieee80211/mac/blockackreordering/BlockAckReordering.h b/src/inet/linklayer/ieee80211/mac/blockackreordering/BlockAckReordering.h
index 42215dd9b40..fdfbd29e226 100644
--- a/src/inet/linklayer/ieee80211/mac/blockackreordering/BlockAckReordering.h
+++ b/src/inet/linklayer/ieee80211/mac/blockackreordering/BlockAckReordering.h
@@ -50,9 +50,11 @@ class INET_API BlockAckReordering
public:
virtual ~BlockAckReordering();
+ ReleasedFrames detachAgreement(MacAddress peer, Tid tid, bool deliverComplete, std::vector& discarded);
+ void clearPeer(MacAddress peer);
void processReceivedDelba(const Ptr& delba);
- ReleasedFrames processReceivedQoSFrame(RecipientBlockAckAgreement *agreement, Packet *dataPacket, const Ptr& dataHeader);
+ ReleasedFrames processReceivedQoSFrame(RecipientBlockAckAgreement *agreement, Packet *dataPacket, const Ptr& dataHeader, bool interframe = false);
ReleasedFrames processReceivedBlockAckReq(RecipientBlockAckAgreement *agreement, const Ptr& blockAckReq);
};
diff --git a/src/inet/linklayer/ieee80211/mac/channelaccess/Edca.h b/src/inet/linklayer/ieee80211/mac/channelaccess/Edca.h
index b7b9e4d6bc2..4b038c0c914 100644
--- a/src/inet/linklayer/ieee80211/mac/channelaccess/Edca.h
+++ b/src/inet/linklayer/ieee80211/mac/channelaccess/Edca.h
@@ -30,11 +30,10 @@ class INET_API Edca : public SimpleModule
virtual int numInitStages() const override { return NUM_INIT_STAGES; }
virtual void initialize(int stage) override;
- virtual AccessCategory mapTidToAc(Tid tid);
-
public:
virtual ~Edca();
+ virtual AccessCategory mapTidToAc(Tid tid);
virtual AccessCategory classifyFrame(const Ptr& header);
virtual Edcaf *getEdcaf(AccessCategory ac) const { return edcafs[ac]; }
EdcaTransmitLifetimeHandler *getLifetimeHandler() const { return lifetimeHandler; }
diff --git a/src/inet/linklayer/ieee80211/mac/channelaccess/Edcaf.cc b/src/inet/linklayer/ieee80211/mac/channelaccess/Edcaf.cc
index 5dc48059089..cb0e9d6549b 100644
--- a/src/inet/linklayer/ieee80211/mac/channelaccess/Edcaf.cc
+++ b/src/inet/linklayer/ieee80211/mac/channelaccess/Edcaf.cc
@@ -165,6 +165,11 @@ bool Edcaf::isInternalCollision()
return collisionController->isInternalCollision(this);
}
+bool Edcaf::hasEligibleFrames() const
+{
+ return frameEligibility ? frameEligibility() : !pendingQueue->isEmpty() || inProgressFrames->getLength() != 0;
+}
+
int Edcaf::getCwMax(AccessCategory ac, int aCwMax, int aCwMin)
{
switch (ac) {
diff --git a/src/inet/linklayer/ieee80211/mac/channelaccess/Edcaf.h b/src/inet/linklayer/ieee80211/mac/channelaccess/Edcaf.h
index c2d716ade33..b9a77e9c051 100644
--- a/src/inet/linklayer/ieee80211/mac/channelaccess/Edcaf.h
+++ b/src/inet/linklayer/ieee80211/mac/channelaccess/Edcaf.h
@@ -49,6 +49,7 @@ class INET_API Edcaf : public IChannelAccess, public IContention::ICallback, pub
InProgressFrames *inProgressFrames = nullptr;
bool owning = false;
+ std::function frameEligibility;
simtime_t slotTime = -1;
simtime_t sifs = -1;
@@ -102,6 +103,8 @@ class INET_API Edcaf : public IChannelAccess, public IContention::ICallback, pub
// Edcaf
virtual bool isOwning() { return owning; }
virtual bool isInternalCollision();
+ void setFrameEligibility(std::function predicate) { frameEligibility = predicate; }
+ bool hasEligibleFrames() const;
virtual AccessCategory getAccessCategory() { return ac; }
};
diff --git a/src/inet/linklayer/ieee80211/mac/contention/EdcaCollisionController.cc b/src/inet/linklayer/ieee80211/mac/contention/EdcaCollisionController.cc
index 33b94c93563..4b3172e039f 100644
--- a/src/inet/linklayer/ieee80211/mac/contention/EdcaCollisionController.cc
+++ b/src/inet/linklayer/ieee80211/mac/contention/EdcaCollisionController.cc
@@ -36,9 +36,9 @@ bool EdcaCollisionController::isInternalCollision(Edcaf *edcaf)
simtime_t now = simTime();
AccessCategory accessCategory = edcaf->getAccessCategory();
auto hasFrames = [](const Edcaf *candidate) {
- return !candidate->getPendingQueue()->isEmpty() || candidate->getInProgressFrames()->getLength() != 0;
+ return candidate->hasEligibleFrames();
};
- // An expired empty queue cannot initiate a TXOP or cause an internal collision.
+ // Deferred or expired DATA cannot initiate a TXOP or cause an internal collision.
// Keep the winner's in-progress frame visible until its transmission completes.
if (txStartTimes[accessCategory] == now && hasFrames(edcaf)) {
for (int ac = accessCategory + 1; ac < 4; ac++) {
diff --git a/src/inet/linklayer/ieee80211/mac/contract/BlockAckVariant.h b/src/inet/linklayer/ieee80211/mac/contract/BlockAckVariant.h
new file mode 100644
index 00000000000..f02e1033036
--- /dev/null
+++ b/src/inet/linklayer/ieee80211/mac/contract/BlockAckVariant.h
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#ifndef __INET_BLOCKACKVARIANT_H
+#define __INET_BLOCKACKVARIANT_H
+
+namespace inet {
+namespace ieee80211 {
+
+enum class BlockAckVariant { BASIC, COMPRESSED };
+
+} // namespace ieee80211
+} // namespace inet
+
+#endif
diff --git a/src/inet/linklayer/ieee80211/mac/contract/IBlockAckAgreementHandlerCallback.h b/src/inet/linklayer/ieee80211/mac/contract/IBlockAckAgreementHandlerCallback.h
index cc4a7b86b92..3fefe1666c0 100644
--- a/src/inet/linklayer/ieee80211/mac/contract/IBlockAckAgreementHandlerCallback.h
+++ b/src/inet/linklayer/ieee80211/mac/contract/IBlockAckAgreementHandlerCallback.h
@@ -18,7 +18,8 @@ class INET_API IBlockAckAgreementHandlerCallback
public:
virtual ~IBlockAckAgreementHandlerCallback() {}
- virtual void scheduleInactivityTimer(simtime_t timeout) = 0;
+ // The coordinator recomputes the earliest absolute deadline across both agreement roles.
+ virtual void scheduleInactivityTimer() = 0;
};
} // namespace ieee80211
diff --git a/src/inet/linklayer/ieee80211/mac/contract/IBlockAckAgreementLifecycle.h b/src/inet/linklayer/ieee80211/mac/contract/IBlockAckAgreementLifecycle.h
new file mode 100644
index 00000000000..46e49e22386
--- /dev/null
+++ b/src/inet/linklayer/ieee80211/mac/contract/IBlockAckAgreementLifecycle.h
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#ifndef __INET_IBLOCKACKAGREEMENTLIFECYCLE_H
+#define __INET_IBLOCKACKAGREEMENTLIFECYCLE_H
+
+#include "inet/linklayer/common/MacAddress.h"
+
+namespace inet {
+namespace ieee80211 {
+
+// Management supplies the old peer identity before association replacement or loss.
+class INET_API IBlockAckAgreementLifecycle
+{
+ public:
+ virtual ~IBlockAckAgreementLifecycle() {}
+ virtual void resetPeerAgreements(const MacAddress& peer) = 0;
+};
+
+} // namespace ieee80211
+} // namespace inet
+
+#endif
diff --git a/src/inet/linklayer/ieee80211/mac/contract/IFrameSequenceHandler.h b/src/inet/linklayer/ieee80211/mac/contract/IFrameSequenceHandler.h
index ee9d4d37465..d2002a5ffdc 100644
--- a/src/inet/linklayer/ieee80211/mac/contract/IFrameSequenceHandler.h
+++ b/src/inet/linklayer/ieee80211/mac/contract/IFrameSequenceHandler.h
@@ -48,6 +48,8 @@ class INET_API IFrameSequenceHandler
virtual void transmissionComplete() = 0;
virtual bool isSequenceRunning() = 0;
virtual void handleStartRxTimeout() = 0;
+ // End a response wait after recipient processing. The sequence suppresses failure for retired DATA or management frames.
+ virtual void interruptFrameSequence() = 0;
};
} // namespace ieee80211
diff --git a/src/inet/linklayer/ieee80211/mac/contract/IOriginatorBlockAckAgreementHandler.h b/src/inet/linklayer/ieee80211/mac/contract/IOriginatorBlockAckAgreementHandler.h
index 4a01e89b6c3..7161304e0ad 100644
--- a/src/inet/linklayer/ieee80211/mac/contract/IOriginatorBlockAckAgreementHandler.h
+++ b/src/inet/linklayer/ieee80211/mac/contract/IOriginatorBlockAckAgreementHandler.h
@@ -22,7 +22,27 @@ namespace ieee80211 {
class INET_API IOriginatorBlockAckAgreementHandler
{
public:
+ typedef std::pair AgreementId;
+ enum class ProcedureState { SETUP_INTENT, REQUEST_PENDING, TEARDOWN_PENDING, RECOVERY_NEEDED, WAITING_FOR_SUCCESSOR };
+ struct Procedure {
+ ProcedureState state = ProcedureState::SETUP_INTENT;
+ bool requestAttempted = false;
+ simtime_t responseDeadline = SIMTIME_MAX;
+ };
virtual ~IOriginatorBlockAckAgreementHandler() {}
+ virtual const std::map& getAgreements() const = 0;
+ virtual const std::map& getProcedures() const = 0;
+ virtual simtime_t computeEarliestExpirationTime() = 0;
+ virtual OriginatorBlockAckAgreement *detachAgreement(MacAddress peer, Tid tid) = 0;
+ virtual std::vector detachPeer(MacAddress peer) = 0;
+ virtual void noteEligibleDataTransmission(MacAddress peer, Tid tid) = 0;
+ virtual void beginSetupIntent(MacAddress peer, Tid tid) = 0;
+ virtual void beginTeardown(MacAddress peer, Tid tid) = 0;
+ virtual void failTeardown(MacAddress peer, Tid tid) = 0;
+ virtual void finishProcedure(MacAddress peer, Tid tid) = 0;
+ virtual void markRequestAttempted(MacAddress peer, Tid tid) = 0;
+ virtual void acknowledgeRequest(MacAddress peer, Tid tid, simtime_t timeout) = 0;
+ virtual void startSetup(MacAddress peer, Tid tid, SequenceNumberCyclic sequence, IOriginatorBlockAckAgreementPolicy *policy, IProcedureCallback *callback, bool aMsdu) = 0;
virtual void processReceivedBlockAck(const Ptr& blockAck, IBlockAckAgreementHandlerCallback *callback) = 0;
virtual void processTransmittedAddbaReq(const Ptr& addbaReq) = 0;
diff --git a/src/inet/linklayer/ieee80211/mac/contract/IOriginatorBlockAckAgreementPolicy.h b/src/inet/linklayer/ieee80211/mac/contract/IOriginatorBlockAckAgreementPolicy.h
index 40ea82aa95e..c6e1ddd9638 100644
--- a/src/inet/linklayer/ieee80211/mac/contract/IOriginatorBlockAckAgreementPolicy.h
+++ b/src/inet/linklayer/ieee80211/mac/contract/IOriginatorBlockAckAgreementPolicy.h
@@ -9,6 +9,7 @@
#define __INET_IORIGINATORBLOCKACKAGREEMENTPOLICY_H
#include "inet/common/packet/Packet.h"
+#include "inet/linklayer/ieee80211/mac/contract/BlockAckVariant.h"
#include "inet/linklayer/ieee80211/mac/Ieee80211Frame_m.h"
namespace inet {
@@ -21,6 +22,8 @@ class INET_API IOriginatorBlockAckAgreementPolicy
public:
virtual ~IOriginatorBlockAckAgreementPolicy() {}
+ virtual bool isProfileAllowed(BlockAckVariant variant, bool localHt, bool peerHt) const = 0;
+
virtual bool isAddbaReqNeeded(Packet *packet, const Ptr& header) = 0;
virtual bool isAddbaReqAccepted(const Ptr& addbaResp, OriginatorBlockAckAgreement *agreement) = 0;
virtual bool isDelbaAccepted(const Ptr& delba) = 0;
diff --git a/src/inet/linklayer/ieee80211/mac/contract/IOriginatorQoSAckPolicy.h b/src/inet/linklayer/ieee80211/mac/contract/IOriginatorQoSAckPolicy.h
index 94a583a111a..641e21e8ecd 100644
--- a/src/inet/linklayer/ieee80211/mac/contract/IOriginatorQoSAckPolicy.h
+++ b/src/inet/linklayer/ieee80211/mac/contract/IOriginatorQoSAckPolicy.h
@@ -10,6 +10,7 @@
#include "inet/linklayer/ieee80211/mac/Ieee80211Frame_m.h"
#include "inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreement.h"
+#include "inet/linklayer/ieee80211/mac/contract/IOriginatorBlockAckAgreementHandler.h"
#include "inet/linklayer/ieee80211/mac/originator/TxopProcedure.h"
#include "inet/linklayer/ieee80211/mac/queue/InProgressFrames.h"
@@ -23,9 +24,9 @@ class INET_API IOriginatorQoSAckPolicy
virtual bool isAckNeeded(const Ptr& header) const = 0;
virtual AckPolicy computeAckPolicy(Packet *packet, const Ptr& header, OriginatorBlockAckAgreement *agreement) const = 0;
- virtual bool isBlockAckReqNeeded(InProgressFrames *inProgressFrames, TxopProcedure *txopProcedure) const = 0;
+ virtual bool isBlockAckReqNeeded(InProgressFrames *inProgressFrames, TxopProcedure *txopProcedure, IOriginatorBlockAckAgreementHandler *agreements) const = 0;
virtual bool isBlockAckPolicyEligibleFrame(Packet *packet, const Ptr& header) const = 0;
- virtual std::tuple computeBlockAckReqParameters(InProgressFrames *inProgressFrames, TxopProcedure *txopProcedure) const = 0;
+ virtual std::tuple computeBlockAckReqParameters(InProgressFrames *inProgressFrames, TxopProcedure *txopProcedure, IOriginatorBlockAckAgreementHandler *agreements) const = 0;
virtual simtime_t getAckTimeout(Packet *packet, const Ptr& dataOrMgmtHeader) const = 0;
virtual simtime_t getBlockAckTimeout(Packet *packet, const Ptr& blockAckReq) const = 0;
diff --git a/src/inet/linklayer/ieee80211/mac/contract/IOriginatorQosMacDataService.h b/src/inet/linklayer/ieee80211/mac/contract/IOriginatorQosMacDataService.h
new file mode 100644
index 00000000000..04f0ecda71b
--- /dev/null
+++ b/src/inet/linklayer/ieee80211/mac/contract/IOriginatorQosMacDataService.h
@@ -0,0 +1,48 @@
+//
+// Copyright (C) 2026 OpenSim Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+
+#ifndef __INET_IORIGINATORQOSMACDATASERVICE_H
+#define __INET_IORIGINATORQOSMACDATASERVICE_H
+
+#include
+
+#include "inet/linklayer/ieee80211/mac/Ieee80211Frame_m.h"
+#include "inet/linklayer/ieee80211/mac/contract/IOriginatorMacDataService.h"
+
+namespace inet {
+namespace ieee80211 {
+
+/** Owns a reversible DATA preview. Source packet identities refer to frames still owned by the queue. */
+struct INET_API PreparedQosData : public cObject
+{
+ std::unique_ptr packet;
+ queueing::IPacketQueue *queue = nullptr;
+ std::vector sourceIds;
+ bool aggregate = false;
+
+ ~PreparedQosData() override;
+ void setPacket(Packet *value);
+ Packet *releasePacket();
+};
+
+class INET_API IOriginatorQosMacDataService : public IOriginatorMacDataService
+{
+ public:
+ // Preview borrows the frame without extraction or sequence assignment.
+ virtual Packet *peekEligibleFrame(queueing::IPacketQueue *queue) const = 0;
+ virtual SequenceNumberCyclic getNextSequenceNumber(MacAddress peer, Tid tid) const = 0;
+ virtual bool hasAMsduAggregation() const = 0;
+ // These QoS DATA methods separate reversible preparation from extraction and sequence assignment.
+ virtual std::unique_ptr prepareFrame(queueing::IPacketQueue *queue, bool allowAggregation) = 0;
+ virtual bool isPreparationValid(const PreparedQosData& preparation, simtime_t start) const = 0;
+ // Success transfers the preview to the caller. Failure retains the preview and all queued sources.
+ virtual Packet *commitPreparedFrame(PreparedQosData& preparation) = 0;
+};
+
+} // namespace ieee80211
+} // namespace inet
+
+#endif
diff --git a/src/inet/linklayer/ieee80211/mac/contract/IQosRateSelection.h b/src/inet/linklayer/ieee80211/mac/contract/IQosRateSelection.h
index 34a38ac1d9a..10c35f613a0 100644
--- a/src/inet/linklayer/ieee80211/mac/contract/IQosRateSelection.h
+++ b/src/inet/linklayer/ieee80211/mac/contract/IQosRateSelection.h
@@ -32,6 +32,9 @@ class INET_API IQosRateSelection
virtual const physicallayer::IIeee80211Mode *computeResponseCtsFrameMode(Packet *packet, const Ptr& rtsFrame, ResponseQueryRole role) = 0;
virtual const physicallayer::IIeee80211Mode *computeResponseAckFrameMode(Packet *packet, const Ptr& dataOrMgmtHeader, ResponseQueryRole role) = 0;
virtual const physicallayer::IIeee80211Mode *computeResponseBlockAckFrameMode(Packet *packet, const Ptr& blockAckReq, ResponseQueryRole role) = 0;
+ // HCF derives startsTxop from the current frame sequence before this query.
+ virtual const physicallayer::IIeee80211Mode *computeCompressedBlockAckReqMode(Packet *packet,
+ const Ptr& blockAckReq, bool startsTxop) = 0;
virtual const physicallayer::IIeee80211Mode *computeMode(Packet *packet, const Ptr& header, TxopProcedure *txopProcedure) = 0;
};
diff --git a/src/inet/linklayer/ieee80211/mac/contract/IRecipientBlockAckAgreementHandler.h b/src/inet/linklayer/ieee80211/mac/contract/IRecipientBlockAckAgreementHandler.h
index 357745c3ddb..5c4beb3fe04 100644
--- a/src/inet/linklayer/ieee80211/mac/contract/IRecipientBlockAckAgreementHandler.h
+++ b/src/inet/linklayer/ieee80211/mac/contract/IRecipientBlockAckAgreementHandler.h
@@ -23,6 +23,13 @@ class INET_API IRecipientBlockAckAgreementHandler
{
public:
virtual ~IRecipientBlockAckAgreementHandler() {}
+ virtual simtime_t computeEarliestExpirationTime() = 0;
+ virtual const std::map, RecipientBlockAckAgreement *>& getAgreements() const = 0;
+ virtual RecipientBlockAckAgreement *detachAgreement(MacAddress peer, Tid tid) = 0;
+ virtual std::vector detachPeer(MacAddress peer) = 0;
+ virtual bool isTeardownPending(MacAddress peer, Tid tid) const = 0;
+ virtual void beginTeardown(MacAddress peer, Tid tid) = 0;
+ virtual void finishTeardown(MacAddress peer, Tid tid) = 0;
virtual void processTransmittedAddbaResp(const Ptr& addbaResp, IBlockAckAgreementHandlerCallback *callback) = 0;
virtual void processReceivedAddbaRequest(const Ptr& addbaRequest, IRecipientBlockAckAgreementPolicy *blockAckAgreementPolicy, IProcedureCallback *callback) = 0;
diff --git a/src/inet/linklayer/ieee80211/mac/contract/IRecipientBlockAckAgreementPolicy.h b/src/inet/linklayer/ieee80211/mac/contract/IRecipientBlockAckAgreementPolicy.h
index 48009a67161..2f429ef7169 100644
--- a/src/inet/linklayer/ieee80211/mac/contract/IRecipientBlockAckAgreementPolicy.h
+++ b/src/inet/linklayer/ieee80211/mac/contract/IRecipientBlockAckAgreementPolicy.h
@@ -9,6 +9,7 @@
#define __INET_IRECIPIENTBLOCKACKAGREEMENTPOLICY_H
#include "inet/linklayer/ieee80211/mac/Ieee80211Frame_m.h"
+#include "inet/linklayer/ieee80211/mac/contract/BlockAckVariant.h"
namespace inet {
namespace ieee80211 {
@@ -18,6 +19,8 @@ class INET_API IRecipientBlockAckAgreementPolicy
public:
virtual ~IRecipientBlockAckAgreementPolicy() {}
+ virtual bool isProfileAllowed(BlockAckVariant variant, bool localHt, bool peerHt) const = 0;
+
virtual bool isAddbaReqAccepted(const Ptr& addbaReq) = 0;
virtual bool isDelbaAccepted(const Ptr& delba) = 0;
diff --git a/src/inet/linklayer/ieee80211/mac/contract/IRecipientQosAckPolicy.h b/src/inet/linklayer/ieee80211/mac/contract/IRecipientQosAckPolicy.h
index ccf47cc5610..42aaecc9db8 100644
--- a/src/inet/linklayer/ieee80211/mac/contract/IRecipientQosAckPolicy.h
+++ b/src/inet/linklayer/ieee80211/mac/contract/IRecipientQosAckPolicy.h
@@ -26,6 +26,8 @@ class INET_API IRecipientQosAckPolicy
virtual simtime_t computeAckDurationField(Packet *packet, const Ptr& header) const = 0;
virtual simtime_t computeBasicBlockAckDurationField(Packet *packet, const Ptr& basicBlockAckReq) const = 0;
+ // The request selects the response variant and its actual airtime.
+ virtual simtime_t computeCompressedBlockAckDurationField(Packet *packet, const Ptr& blockAckReq) const = 0;
};
} // namespace ieee80211
diff --git a/src/inet/linklayer/ieee80211/mac/contract/IRecipientQosMacDataService.h b/src/inet/linklayer/ieee80211/mac/contract/IRecipientQosMacDataService.h
index 8a3ff4c0b7c..7aded713794 100644
--- a/src/inet/linklayer/ieee80211/mac/contract/IRecipientQosMacDataService.h
+++ b/src/inet/linklayer/ieee80211/mac/contract/IRecipientQosMacDataService.h
@@ -23,8 +23,11 @@ class INET_API IRecipientQosMacDataService
public:
virtual ~IRecipientQosMacDataService() {}
+ virtual std::vector retireAgreement(RecipientBlockAckAgreement *agreement, bool associationReset) = 0;
+ virtual void clearPeer(MacAddress peer) = 0;
- virtual std::vector dataFrameReceived(Packet *dataPacket, const Ptr& dataHeader, IRecipientBlockAckAgreementHandler *blockAckAgreementHandler) = 0;
+ // HCF supplies the observed SIFS or permitted RIFS relation for this frame.
+ virtual std::vector dataFrameReceived(Packet *dataPacket, const Ptr& dataHeader, IRecipientBlockAckAgreementHandler *blockAckAgreementHandler, bool interframe = false) = 0;
virtual std::vector controlFrameReceived(Packet *controlPacket, const Ptr& controlHeader, IRecipientBlockAckAgreementHandler *blockAckAgreementHandler) = 0;
virtual std::vector managementFrameReceived(Packet *mgmtPacket, const Ptr& mgmtHeader) = 0;
};
diff --git a/src/inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.cc b/src/inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.cc
index a05c4f1d269..601eb39739e 100644
--- a/src/inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.cc
+++ b/src/inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.cc
@@ -10,6 +10,7 @@
#include "inet/common/ModuleAccess.h"
#include "inet/common/Simsignals.h"
#include "inet/linklayer/ieee80211/mac/Ieee80211Mac.h"
+#include "inet/linklayer/ieee80211/mac/Ieee80211DataSequenceTag_m.h"
#include "inet/linklayer/ieee80211/mac/contract/FrameTransmissionDetails_m.h"
#include "inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreementHandler.h"
#include "inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckProcedure.h"
@@ -17,9 +18,17 @@
#include "inet/linklayer/ieee80211/mac/framesequence/FrameSequenceStep.h"
#include "inet/linklayer/ieee80211/mac/framesequence/HcfFs.h"
#include "inet/linklayer/ieee80211/mac/rateselection/RateSelection.h"
+#include "inet/linklayer/ieee80211/mac/rateselection/Ieee80211PeerModeSelection.h"
#include "inet/linklayer/ieee80211/mac/recipient/RecipientAckProcedure.h"
#include "inet/linklayer/ieee80211/mgmt/Ieee80211MgmtTransactionTag_m.h"
+#include "inet/linklayer/ieee80211/mgmt/Ieee80211MgmtFrame_m.h"
+#include "inet/linklayer/ieee80211/mib/Ieee80211Mib.h"
+#include "inet/queueing/contract/ISelectivePacketCollection.h"
#include "inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211Tag_m.h"
+#include "inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211Transmission.h"
+#include "inet/physicallayer/wireless/common/contract/packetlevel/IRadioMedium.h"
+#include "inet/physicallayer/wireless/common/contract/packetlevel/IReception.h"
+#include "inet/physicallayer/wireless/common/contract/packetlevel/SignalTag_m.h"
namespace inet {
namespace ieee80211 {
@@ -43,12 +52,12 @@ void Hcf::initialize(int stage)
blockAckVariant = BlockAckVariant::COMPRESSED;
else
throw cRuntimeError("Unknown blockAckVariant \"%s\": expected \"basic\" or \"compressed\"", configuredVariant);
- if (blockAckVariant == BlockAckVariant::COMPRESSED)
- throw cRuntimeError("Compressed Block Ack exchanges are not implemented");
mac = check_and_cast(getContainingNicModule(this)->getSubmodule("mac"));
+ mib = check_and_cast(mac->getModuleByPath(mac->par("mibModule")));
startRxTimer = new cMessage("startRxTimeout");
inactivityTimer = new cMessage("blockAckInactivityTimer");
dataLifetimeTimer = new cMessage("dataLifetimeTimer");
+ addbaResponseTimer = new cMessage("addbaResponseTimer");
edca = check_and_cast(getSubmodule("edca"));
hcca = check_and_cast(getSubmodule("hcca"));
tx = check_and_cast(getModuleByPath(par("txModule")));
@@ -59,6 +68,7 @@ void Hcf::initialize(int stage)
rateSelection = check_and_cast(getSubmodule("rateSelection"));
frameSequenceHandler = new FrameSequenceHandler();
WATCH_EXPR("frameSequenceInfo", getFrameSequenceInfo());
+ WATCH_EXPR("numPendingInterframeReceptions", pendingReceptions.size());
originatorDataService = check_and_cast(getSubmodule(("originatorMacDataService")));
recipientDataService = check_and_cast(getSubmodule("recipientMacDataService"));
originatorAckPolicy = check_and_cast(getSubmodule("originatorAckPolicy"));
@@ -77,16 +87,47 @@ void Hcf::initialize(int stage)
}
}
else if (stage == INITSTAGE_LAST) {
+ if (blockAckVariant == BlockAckVariant::COMPRESSED && par("isBlockAckSupported").boolValue()) {
+ interframeRadio = check_and_cast(mac->gate("lowerLayerOut")->getNextGate()->getOwnerModule());
+ auto radioModule = check_and_cast(interframeRadio.get());
+ for (auto signal : {receptionStartedSignal, receptionEndedSignal, transmissionStartedSignal, transmissionEndedSignal, IRadio::receptionStateChangedSignal})
+ radioModule->subscribe(signal, this);
+ }
+ if (par("isBlockAckSupported").boolValue()) {
+ EV_INFO << "Block Ack profile: " << (blockAckVariant == BlockAckVariant::BASIC ? "basic" : "compressed") << endl;
+ if (blockAckVariant == BlockAckVariant::COMPRESSED) {
+ if (originatorBlockAckAgreementPolicy && originatorBlockAckAgreementPolicy->isDelayedAckPolicySupported())
+ throw cRuntimeError("Compressed Block Ack does not support delayedAckPolicySupported in %s.originatorBlockAckAgreementPolicy", getFullPath().c_str());
+ if (recipientBlockAckAgreementPolicy && recipientBlockAckAgreementPolicy->delayedBlockAckPolicySupported())
+ throw cRuntimeError("Compressed Block Ack does not support delayedAckPolicySupported in %s.recipientBlockAckAgreementPolicy", getFullPath().c_str());
+ if (!mib->isHtOperationSupported())
+ EV_WARN << "Compressed Block Ack has no usable local HT support; DATA uses Normal Ack. Set blockAckVariant = basic for Basic compatibility.\n";
+ }
+ }
// Edca resolves its Edcaf array at the link-layer stage. Install the
// queue signal listeners after all child initialization has completed.
auto lifetimeHandler = edca->getLifetimeHandler();
lifetimeHandler->setDeadlineChangedCallback([this]() { scheduleDataLifetimeTimer(); });
check_and_cast(originatorDataService)->setLifetimeHandler(
lifetimeHandler, [this]() { expireDataFrames(); });
+ check_and_cast(originatorDataService)->setEligibility(
+ [this](Packet *packet) { return isFrameEligible(packet, false); },
+ [this](Packet *packet) { return isAMsduPermitted(packet); });
+ check_and_cast(originatorDataService)->setFragmentationPermission([this](Packet *packet) {
+ auto data = dynamicPtrCast(packet->peekAtFront());
+ if (blockAckVariant != BlockAckVariant::COMPRESSED || !data || data->getType() != ST_DATA_WITH_QOS || !originatorBlockAckAgreementHandler)
+ return true;
+ auto agreement = originatorBlockAckAgreementHandler->getAgreement(data->getReceiverAddress(), data->getTid());
+ return !agreement || !agreement->getIsAddbaResponseReceived();
+ });
+ mib->subscribe(Ieee80211Mib::rateStateChangedSignal, this);
for (int ac = 0; ac < AC_NUMCATEGORIES; ac++) {
+ edca->getEdcaf(static_cast(ac))->setFrameEligibility(
+ [this, ac]() { return hasEligibleFrameToTransmit(static_cast(ac)); });
check_and_cast(edca->getEdcaf(static_cast(ac))->getPendingQueue())->subscribe(packetDroppedSignal, this);
auto frames = edca->getEdcaf(static_cast(ac))->getInProgressFrames();
frames->setLifetimeHandler(lifetimeHandler);
+ frames->setTransmissionEligibility([this](Packet *packet) { return isFrameEligible(packet, true); });
frames->subscribe(InProgressFrames::packetDequeuedSignal, this);
}
}
@@ -179,6 +220,8 @@ void Hcf::expireDataFrames(bool preserveSequence)
PacketDropDetails details;
details.setReason(LIFETIME_EXPIRED);
emit(packetDroppedSignal, packet, &details);
+ if (inProgress)
+ firstDataDiscarded(header);
if (!inProgress)
delete packet;
}
@@ -189,6 +232,7 @@ void Hcf::expireDataFrames(bool preserveSequence)
frames->clearDroppedFrames();
}
scheduleDataLifetimeTimer();
+ advanceOriginatorWindows();
}
void Hcf::forEachChild(cVisitor *v)
@@ -200,8 +244,24 @@ void Hcf::forEachChild(cVisitor *v)
void Hcf::handleMessage(cMessage *msg)
{
- if (msg == dataLifetimeTimer)
+ if (msg == addbaResponseTimer) {
+ std::vector> expired;
+ for (const auto& entry : originatorBlockAckAgreementHandler->getProcedures()) {
+ if (entry.second.responseDeadline <= simTime())
+ expired.push_back(entry.first);
+ }
+ for (const auto& id : expired)
+ beginTeardown(id.first, id.second, true, 39);
+ scheduleAddbaResponseTimer();
+ updateContention();
+ }
+ else if (msg == dataLifetimeTimer) {
expireDataFrames();
+ if (!frameSequenceHandler->isSequenceRunning())
+ completeTransfers();
+ updateAgreementProcedures();
+ updateContention();
+ }
else if (msg == startRxTimer) {
if (!isReceptionInProgress()) {
frameSequenceHandler->handleStartRxTimeout();
@@ -209,8 +269,19 @@ void Hcf::handleMessage(cMessage *msg)
}
else if (msg == inactivityTimer) {
if (originatorBlockAckAgreementHandler && recipientBlockAckAgreementHandler) {
- originatorBlockAckAgreementHandler->blockAckAgreementExpired(this, this);
- recipientBlockAckAgreementHandler->blockAckAgreementExpired(this, this);
+ std::vector> expired;
+ for (const auto& entry : originatorBlockAckAgreementHandler->getAgreements()) {
+ if (entry.second->getIsAddbaResponseReceived() && entry.second->getExpirationTime() <= simTime())
+ expired.push_back({entry.first.first, entry.first.second, true});
+ }
+ for (const auto& entry : recipientBlockAckAgreementHandler->getAgreements()) {
+ if (entry.second->getExpirationTime() <= simTime())
+ expired.push_back({entry.first.first, entry.first.second, false});
+ }
+ for (const auto& entry : expired)
+ beginTeardown(std::get<0>(entry), std::get<1>(entry), std::get<2>(entry), 39);
+ scheduleInactivityTimer();
+ updateContention();
}
else
throw cRuntimeError("Unknown event");
@@ -255,7 +326,8 @@ void Hcf::processUpperFrame(Packet *packet, const PtrgetType() == ST_DATA_WITH_QOS)
edca->getLifetimeHandler()->registerFrame(packet, ac);
pendingQueue->enqueuePacket(packet);
- if (!pendingQueue->isEmpty()) {
+ updateAgreementProcedures();
+ if (!pendingQueue->isEmpty() && !handlingAgreementAction && hasFrameToTransmit(ac)) {
auto edcaf = edca->getChannelOwner();
if (edcaf == nullptr || edcaf->getAccessCategory() != ac) {
EV_DETAIL << "Requesting channel for access category " << printAccessCategory(ac) << endl;
@@ -266,7 +338,59 @@ void Hcf::processUpperFrame(Packet *packet, const Ptr(interframeRadio.get()) && signalID == receptionStartedSignal) {
+ auto reception = check_and_cast(obj);
+ auto transmission = reception->getTransmission();
+ std::set active;
+ for (auto ongoing : interframeRadio->getReceptionsInProgress())
+ active.insert(ongoing->getId());
+ for (auto it = pendingReceptions.begin(); it != pendingReceptions.end();) {
+ if (!it->second.completed && !active.count(it->first))
+ it = pendingReceptions.erase(it);
+ else
+ ++it;
+ }
+ PendingReception pending;
+ auto medium = interframeRadio->getMedium();
+ pending.timing = medium->getTransmissionTimingSnapshot(transmission->getId());
+ auto wifi = dynamic_cast(transmission);
+ pending.ht = wifi && wifi->getMode()->getModulationClass() == IIeee80211Mode::ModulationClass::HT;
+ if (wifi)
+ pending.extension = wifi->getSignalExtensionDuration();
+ auto gap = medium->computePrecedingSignalGap(transmission->getId(), interframeHistory.timing);
+ if (gap && (interframeHistory.mpdu || interframeHistory.ack)) {
+ // IEEE Std 802.11-2024, 10.3.2.3.3: this model uses coverage class zero.
+ pending.sifs = std::abs((*gap - modeSet->getSifsTime()).raw()) <= modeSet->getSlotTime().raw() / 10;
+ if (interframeHistory.mpdu && interframeHistory.ht && pending.ht && interframeHistory.noSifsResponse &&
+ interframeHistory.timing.transmitterRadioId == pending.timing.transmitterRadioId && interframeHistory.extension) {
+ auto rifs = wifi->getMode()->getRifsTime();
+ // IEEE Std 802.11-2024, 10.3.2.3.2 and 10.3.8: RIFS excludes the actual preceding extension.
+ pending.rifs = std::abs((*gap + *interframeHistory.extension - rifs).raw()) <= rifs.raw() / 10;
+ pending.precedingReceiver = interframeHistory.receiver;
+ }
+ }
+ interframeHistory = {};
+ pendingReceptions.insert_or_assign(transmission->getId(), std::move(pending));
+ }
+ else if (interframeRadio && source == dynamic_cast(interframeRadio.get()) && signalID == receptionEndedSignal) {
+ auto reception = check_and_cast(obj);
+ auto pending = pendingReceptions.find(reception->getTransmission()->getId());
+ if (pending != pendingReceptions.end())
+ pending->second.completed = true;
+ }
+ else if (interframeRadio && source == dynamic_cast(interframeRadio.get()) && signalID == transmissionStartedSignal)
+ clearInterframeHistory();
+ else if (interframeRadio && source == dynamic_cast(interframeRadio.get()) && signalID == transmissionEndedSignal) {
+ clearInterframeHistory();
+ auto transmission = check_and_cast(obj);
+ if (completedTransmissionWasAck) {
+ interframeHistory.timing = interframeRadio->getMedium()->getTransmissionTimingSnapshot(transmission->getId());
+ interframeHistory.ack = true;
+ }
+ completedTransmissionWasAck = false;
+ }
+ else if (signalID == InProgressFrames::packetDequeuedSignal) {
Enter_Method("packetDequeued");
edca->getLifetimeHandler()->forgetFrame(check_and_cast(obj)->getId());
}
@@ -274,6 +398,7 @@ void Hcf::receiveSignal(cComponent *source, simsignal_t signalID, cObject *obj,
Enter_Method("%s", cComponent::getSignalName(signalID));
auto packet = check_and_cast(obj);
edca->getLifetimeHandler()->forgetFrame(packet->getId());
+ actionTerminated(packet, false);
if (packet->findTag() != nullptr) {
FrameTransmissionDetails transmissionDetails;
transmissionDetails.setStatus(FRAME_TRANSMISSION_STATUS_DROPPED_BEFORE_TRANSMISSION);
@@ -284,24 +409,614 @@ void Hcf::receiveSignal(cComponent *source, simsignal_t signalID, cObject *obj,
ModeSetListener::receiveSignal(source, signalID, obj, details);
}
+void Hcf::receiveSignal(cComponent *source, simsignal_t signalID, intval_t value, cObject *details)
+{
+ Enter_Method_Silent();
+ if (interframeRadio && source == dynamic_cast(interframeRadio.get()) && signalID == IRadio::receptionStateChangedSignal &&
+ value != IRadio::RECEPTION_STATE_RECEIVING) {
+ bool incomplete = false;
+ for (auto it = pendingReceptions.begin(); it != pendingReceptions.end();) {
+ if (!it->second.completed) {
+ incomplete = true;
+ it = pendingReceptions.erase(it);
+ }
+ else
+ ++it;
+ }
+ if (incomplete || value == IRadio::RECEPTION_STATE_BUSY || value == IRadio::RECEPTION_STATE_UNDEFINED)
+ interframeHistory = {};
+ }
+}
+
+void Hcf::clearInterframeHistory()
+{
+ interframeHistory = {};
+ pendingReceptions.clear();
+}
+
+bool Hcf::receiveInterframe(Packet *packet, const Ptr& header)
+{
+ auto time = packet->findTag();
+ if (!time)
+ return false;
+ auto entry = pendingReceptions.find(time->getTransmissionId());
+ if (entry == pendingReceptions.end() || !entry->second.completed)
+ return false;
+ auto pending = std::move(entry->second);
+ pendingReceptions.erase(entry);
+ // IEEE Std 802.11-2024, 10.27.3.1, 10.27.3.3, and 11.38.1: the BSS controls RIFS permission.
+ bool rifsPermitted = pending.rifs && modeSet->getPhyType() != Ieee80211ModeSet::PhyType::VHT &&
+ (mib->mode == Ieee80211Mib::INDEPENDENT || mib->mode == Ieee80211Mib::MESH || mib->getHtOperation().rifsMode);
+ bool related = pending.sifs || (pending.rifs && rifsPermitted && pending.precedingReceiver == header->getReceiverAddress());
+ interframeHistory.timing = std::move(pending.timing);
+ interframeHistory.mpdu = true;
+ interframeHistory.ht = pending.ht;
+ interframeHistory.receiver = header->getReceiverAddress();
+ interframeHistory.extension = pending.extension;
+ auto dataOrMgmt = dynamicPtrCast(header);
+ interframeHistory.noSifsResponse = dataOrMgmt ? !recipientAckPolicy->isAckNeeded(dataOrMgmt) :
+ !dynamicPtrCast(header) && !dynamicPtrCast(header);
+ return related;
+}
+
void Hcf::scheduleStartRxTimer(simtime_t timeout)
{
Enter_Method("scheduleStartRxTimer");
scheduleAfter(timeout, startRxTimer);
}
-void Hcf::scheduleInactivityTimer(simtime_t timeout)
+void Hcf::scheduleInactivityTimer()
{
Enter_Method("scheduleInactivityTimer");
- rescheduleAfter(timeout, inactivityTimer);
+ // IEEE Std 802.11-2024, 11.5.4 requires inactivity expiration. HCF reads each role's absolute deadline.
+ simtime_t deadline = SIMTIME_MAX;
+ if (originatorBlockAckAgreementHandler && recipientBlockAckAgreementHandler)
+ deadline = std::min(originatorBlockAckAgreementHandler->computeEarliestExpirationTime(),
+ recipientBlockAckAgreementHandler->computeEarliestExpirationTime());
+ cancelEvent(inactivityTimer);
+ if (deadline != SIMTIME_MAX)
+ scheduleAt(std::max(simTime(), deadline), inactivityTimer);
+}
+
+void Hcf::scheduleAddbaResponseTimer()
+{
+ simtime_t deadline = SIMTIME_MAX;
+ if (originatorBlockAckAgreementHandler) {
+ for (const auto& entry : originatorBlockAckAgreementHandler->getProcedures())
+ deadline = std::min(deadline, entry.second.responseDeadline);
+ }
+ cancelEvent(addbaResponseTimer);
+ if (deadline != SIMTIME_MAX)
+ scheduleAt(std::max(simTime(), deadline), addbaResponseTimer);
+}
+
+bool Hcf::isProfileAllowed(MacAddress peer, bool originator) const
+{
+ auto state = mib->findPeerHtState(peer);
+ bool peerHt = state && state->valid && (originator ? state->negotiatedCapabilities.localTxPeerRx.valid :
+ state->negotiatedCapabilities.localRxPeerTx.valid);
+ return originator ? originatorBlockAckAgreementPolicy->isProfileAllowed(blockAckVariant, mib->isHtOperationSupported(), peerHt) :
+ recipientBlockAckAgreementPolicy->isProfileAllowed(blockAckVariant, mib->isHtOperationSupported(), peerHt);
+}
+
+bool Hcf::hasObsoleteActionInFlight(MacAddress peer, Tid tid, bool originator) const
+{
+ if (finishingSequence || !frameSequenceHandler->isSequenceRunning())
+ return false;
+ auto context = frameSequenceHandler->getContext();
+ for (int i = 0; i < context->getNumSteps(); i++) {
+ auto step = dynamic_cast(context->getStep(i));
+ if (!step)
+ continue;
+ auto packet = step->getFrameToTransmit();
+ if (!obsoleteActions.count(packet->getId()))
+ continue;
+ auto header = packet->peekAtFront();
+ if (header->getReceiverAddress() != peer)
+ continue;
+ if (auto request = dynamicPtrCast(header)) {
+ if (originator && request->getTid() == tid)
+ return true;
+ }
+ else if (auto response = dynamicPtrCast(header)) {
+ if (!originator && response->getTid() == tid)
+ return true;
+ }
+ else if (auto delba = dynamicPtrCast(header)) {
+ if (delba->getInitiator() == originator && delba->getTid() == tid)
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Hcf::isFrameEligible(Packet *packet, bool numbered) const
+{
+ if (obsoleteActions.count(packet->getId()))
+ return false;
+ auto data = dynamicPtrCast(packet->peekAtFront());
+ if (!data || data->getType() != ST_DATA_WITH_QOS || !originatorBlockAckAgreementHandler)
+ return true;
+ auto peer = data->getReceiverAddress();
+ auto tid = data->getTid();
+ if (hasObsoleteActionInFlight(peer, tid, true))
+ return false;
+ const auto& procedures = originatorBlockAckAgreementHandler->getProcedures();
+ auto it = procedures.find({peer, tid});
+ if (it == procedures.end())
+ return true;
+ if (it->second.state == IOriginatorBlockAckAgreementHandler::ProcedureState::WAITING_FOR_SUCCESSOR)
+ return true;
+ return numbered && it->second.state == IOriginatorBlockAckAgreementHandler::ProcedureState::SETUP_INTENT;
+}
+
+bool Hcf::isAMsduPermitted(Packet *packet) const
+{
+ auto data = dynamicPtrCast(packet->peekAtFront());
+ if (!data || !originatorBlockAckAgreementHandler)
+ return true;
+ auto agreement = originatorBlockAckAgreementHandler->getAgreement(data->getReceiverAddress(), data->getTid());
+ return !agreement || !agreement->getIsAddbaResponseReceived() || agreement->isAMsduPermitted();
+}
+
+bool Hcf::hasQueuedData(MacAddress peer, Tid tid) const
+{
+ auto matches = [this, peer, tid](Packet *packet) {
+ auto data = dynamicPtrCast(packet->peekAtFront());
+ return data && data->getType() == ST_DATA_WITH_QOS && data->getReceiverAddress() == peer && data->getTid() == tid &&
+ !edca->getLifetimeHandler()->isExpired(packet);
+ };
+ // Check the TID's category first. Other categories retain support for custom classification.
+ int first = edca->mapTidToAc(tid);
+ for (int offset = 0; offset < AC_NUMCATEGORIES; offset++) {
+ int ac = (first + offset) % AC_NUMCATEGORIES;
+ auto queue = edca->getEdcaf(static_cast(ac))->getPendingQueue();
+ if (auto selected = dynamic_cast(queue)) {
+ try {
+ if (selected->peekEligiblePacket(matches))
+ return true;
+ continue;
+ }
+ catch (const queueing::UnsupportedPacketSelection&) {
+ // Existence checks also support queues without optional selected extraction.
+ }
+ }
+ for (int i = 0; i < queue->getNumPackets(); i++) {
+ if (matches(queue->getPacket(i)))
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Hcf::hasNumberedData(MacAddress peer, Tid tid) const
+{
+ for (int ac = 0; ac < AC_NUMCATEGORIES; ac++) {
+ auto frames = edca->getEdcaf(static_cast(ac))->getInProgressFrames();
+ for (int i = 0; i < frames->getLength(); i++) {
+ auto data = dynamicPtrCast(frames->getFrames(i)->peekAtFront());
+ if (data && data->getReceiverAddress() == peer && data->getTid() == tid)
+ return true;
+ }
+ }
+ return false;
+}
+
+void Hcf::updateAgreementProcedures()
+{
+ if (!originatorBlockAckAgreementHandler || updatingProcedures)
+ return;
+ updatingProcedures = true;
+ auto procedures = originatorBlockAckAgreementHandler->getProcedures();
+ for (const auto& entry : procedures) {
+ auto peer = entry.first.first;
+ auto tid = entry.first.second;
+ auto state = entry.second.state;
+ using State = IOriginatorBlockAckAgreementHandler::ProcedureState;
+ bool queued = hasQueuedData(peer, tid);
+ // INET setup policy waits for a successor after the first eligible DATA transmission.
+ if (state == State::WAITING_FOR_SUCCESSOR) {
+ if (!queued)
+ continue;
+ originatorBlockAckAgreementHandler->beginSetupIntent(peer, tid);
+ state = State::SETUP_INTENT;
+ }
+ if (state == State::SETUP_INTENT) {
+ if (!queued)
+ originatorBlockAckAgreementHandler->finishProcedure(peer, tid);
+ else if (!hasNumberedData(peer, tid) && !hasObsoleteActionInFlight(peer, tid, true)) {
+ auto service = check_and_cast(originatorDataService);
+ originatorBlockAckAgreementHandler->startSetup(peer, tid, service->getNextSequenceNumber(peer, tid),
+ originatorBlockAckAgreementPolicy, this, service->hasAMsduAggregation());
+ }
+ }
+ else if (state == State::REQUEST_PENDING && !queued) {
+ if (entry.second.requestAttempted)
+ beginTeardown(peer, tid, true, 37);
+ else {
+ originatorBlockAckAgreementHandler->finishProcedure(peer, tid);
+ retireAgreement(peer, tid, true);
+ }
+ }
+ else if (state == State::RECOVERY_NEEDED && queued)
+ beginTeardown(peer, tid, true, 37);
+ }
+ updatingProcedures = false;
+ scheduleAddbaResponseTimer();
+}
+
+void Hcf::updateContention()
+{
+ if (handlingAgreementAction)
+ return;
+ for (int ac = 0; ac < AC_NUMCATEGORIES; ac++) {
+ auto edcaf = edca->getEdcaf(static_cast(ac));
+ if (edcaf->isOwning())
+ continue;
+ if (hasFrameToTransmit(static_cast(ac)))
+ edcaf->requestChannel(this);
+ }
+}
+
+void Hcf::advanceOriginatorWindows()
+{
+ if (!originatorBlockAckAgreementHandler)
+ return;
+ auto service = check_and_cast(originatorDataService);
+ for (const auto& entry : originatorBlockAckAgreementHandler->getAgreements()) {
+ auto agreement = entry.second;
+ if (!agreement->getIsAddbaResponseReceived())
+ continue;
+ auto next = service->getNextSequenceNumber(entry.first.first, entry.first.second);
+ std::vector unresolvedSequences;
+ for (int ac = 0; ac < AC_NUMCATEGORIES; ac++) {
+ auto frames = edca->getEdcaf(static_cast(ac))->getInProgressFrames();
+ for (int i = 0; i < frames->getLength(); i++) {
+ auto data = dynamicPtrCast(frames->getFrames(i)->peekAtFront());
+ if (data && data->getType() == ST_DATA_WITH_QOS &&
+ data->getReceiverAddress() == entry.first.first && data->getTid() == entry.first.second)
+ unresolvedSequences.push_back(data->getSequenceNumber());
+ }
+ }
+ // IEEE Std 802.11-2024, 10.25.6.7.1: resolved positions permit transmit-window progress.
+ agreement->advanceWindow(next, unresolvedSequences);
+ }
+}
+
+void Hcf::completeTransfers()
+{
+ if (!originatorBlockAckAgreementHandler)
+ return;
+ std::vector> completed;
+ for (const auto& entry : originatorBlockAckAgreementHandler->getAgreements()) {
+ auto agreement = entry.second;
+ if (agreement->getIsAddbaResponseReceived() && agreement->getFirstDataTransmitted() &&
+ !hasNumberedData(entry.first.first, entry.first.second) && !hasQueuedData(entry.first.first, entry.first.second))
+ completed.push_back(entry.first);
+ }
+ // IEEE Std 802.11-2024, 10.25.4; INET uses the accepted queue predicate at an exchange boundary.
+ for (const auto& id : completed)
+ beginTeardown(id.first, id.second, true, 37);
+}
+
+void Hcf::discardAgreementData(MacAddress peer, Tid tid, bool all, int64_t highestTransmittedDataIndex)
+{
+ if (frameSequenceHandler->isSequenceRunning()) {
+ auto prepared = frameSequenceHandler->getContext()->getQoSContext()->preparedData.get();
+ if (prepared) {
+ auto header = prepared->frame->peekAtFront();
+ if (header->getReceiverAddress() == peer && header->getTid() == tid)
+ prepared->cancelled = true;
+ }
+ }
+ for (int ac = 0; ac < AC_NUMCATEGORIES; ac++) {
+ auto edcaf = edca->getEdcaf(static_cast(ac));
+ auto frames = edcaf->getInProgressFrames();
+ std::vector discard;
+ for (int i = 0; i < frames->getLength(); i++) {
+ auto packet = frames->getFrames(i);
+ auto data = dynamicPtrCast(packet->peekAtFront());
+ bool transmitted = data && edcaf->getAckHandler()->getQoSDataAckStatus(data) != QosAckHandler::Status::FRAME_NOT_YET_TRANSMITTED;
+ if (data && frameSequenceHandler->isSequenceRunning()) {
+ auto context = frameSequenceHandler->getContext();
+ for (int j = 0; j < context->getNumSteps(); j++) {
+ if (auto step = dynamic_cast(context->getStep(j)))
+ transmitted |= step->getFrameToTransmit() == packet;
+ }
+ }
+ // IEEE Std 802.11-2024, 11.5.3.4: the next MPDU must follow the highest transmitted sequence.
+ // Include unsent fragments even if ACK processing already removed an earlier fragment.
+ // Local allocation indices preserve order across gaps and wire sequence wraparound.
+ if (data && data->getReceiverAddress() == peer && data->getTid() == tid &&
+ (all || transmitted || (highestTransmittedDataIndex >= 0 &&
+ packet->getTag()->getAllocationIndex() <= highestTransmittedDataIndex)))
+ discard.push_back(packet);
+ }
+ for (auto packet : discard) {
+ auto data = packet->peekAtFront();
+ edcaf->getAckHandler()->dropFrame(data);
+ edcaf->getRecoveryProcedure()->dropFrame(data);
+ frames->dropFrame(packet);
+ PacketDropDetails details;
+ details.setReason(OTHER_PACKET_DROP);
+ emit(packetDroppedSignal, packet, &details);
+ }
+ if (all) {
+ auto queue = edcaf->getPendingQueue();
+ discard.clear();
+ for (int i = 0; i < queue->getNumPackets(); i++) {
+ auto packet = queue->getPacket(i);
+ auto data = dynamicPtrCast(packet->peekAtFront());
+ if (data && data->getReceiverAddress() == peer && data->getTid() == tid)
+ discard.push_back(packet);
+ }
+ for (auto packet : discard) {
+ queue->removePacket(packet);
+ take(packet);
+ edca->getLifetimeHandler()->forgetFrame(packet->getId());
+ PacketDropDetails details;
+ details.setReason(OTHER_PACKET_DROP);
+ emit(packetDroppedSignal, packet, &details);
+ delete packet;
+ }
+ }
+ if (!frameSequenceHandler->isSequenceRunning() || frameSequenceHandler->getContext()->getInProgressFrames() != frames)
+ frames->clearDroppedFrames();
+ }
+}
+
+void Hcf::firstDataDiscarded(const Ptr& header)
+{
+ if (!originatorBlockAckAgreementHandler)
+ return;
+ auto agreement = originatorBlockAckAgreementHandler->getAgreement(header->getReceiverAddress(), header->getTid());
+ if (agreement && agreement->getIsAddbaResponseReceived() && !agreement->getFirstDataTransmitted() &&
+ agreement->getStartingSequenceNumber() == header->getSequenceNumber())
+ beginTeardown(header->getReceiverAddress(), header->getTid(), true, 37);
+}
+
+void Hcf::invalidateActions(MacAddress peer, Tid tid, bool originator)
+{
+ std::set invalid;
+ std::set attempted;
+ std::set rtsRecoveryOwned;
+ for (auto it = ownedActions.begin(); it != ownedActions.end();) {
+ if (it->second.peer == peer && it->second.tid == tid && it->second.originator == originator) {
+ invalid.insert(it->first);
+ if (it->second.attempted)
+ attempted.insert(it->first);
+ if (it->second.rtsRecoveryOwned)
+ rtsRecoveryOwned.insert(it->first);
+ obsoleteActions.insert(it->first);
+ it = ownedActions.erase(it);
+ }
+ else
+ ++it;
+ }
+ for (int ac = 0; ac < AC_NUMCATEGORIES; ac++) {
+ auto edcaf = edca->getEdcaf(static_cast(ac));
+ auto queue = edcaf->getPendingQueue();
+ std::vector removed;
+ for (int i = 0; i < queue->getNumPackets(); i++) {
+ if (invalid.count(queue->getPacket(i)->getId()))
+ removed.push_back(queue->getPacket(i));
+ }
+ for (auto packet : removed) {
+ queue->removePacket(packet);
+ take(packet);
+ obsoleteActions.erase(packet->getId());
+ delete packet;
+ }
+ auto frames = edcaf->getInProgressFrames();
+ removed.clear();
+ for (int i = 0; i < frames->getLength(); i++) {
+ if (invalid.count(frames->getFrames(i)->getId()))
+ removed.push_back(frames->getFrames(i));
+ }
+ for (auto packet : removed) {
+ auto header = packet->peekAtFront();
+ edcaf->getAckHandler()->dropFrame(header);
+ if (attempted.count(packet->getId()))
+ edcaf->getMgmtAndNonQoSRecoveryProcedure()->dropFrame(packet, header);
+ if (rtsRecoveryOwned.count(packet->getId()))
+ edcaf->getMgmtAndNonQoSRecoveryProcedure()->dropRtsProtectedFrame(header);
+ frames->dropFrame(packet);
+ if (!frameSequenceHandler->isSequenceRunning() || frameSequenceHandler->getContext()->getInProgressFrames() != frames)
+ obsoleteActions.erase(packet->getId());
+ }
+ if (!frameSequenceHandler->isSequenceRunning() || frameSequenceHandler->getContext()->getInProgressFrames() != frames)
+ frames->clearDroppedFrames();
+ }
+}
+
+void Hcf::retireAgreement(MacAddress peer, Tid tid, bool originator, bool associationReset)
+{
+ if (!originator)
+ clearInterframeHistory();
+ if (originator) {
+ auto agreement = originatorBlockAckAgreementHandler->detachAgreement(peer, tid);
+ invalidateActions(peer, tid, originator);
+ discardAgreementData(peer, tid, associationReset, agreement ? agreement->getHighestTransmittedDataIndex() : -1);
+ if (agreement) {
+ if (agreement->getIsAddbaResponseReceived())
+ emit(blockAckAgreementDeletedSignal, agreement);
+ delete agreement;
+ }
+ }
+ else {
+ auto agreement = recipientBlockAckAgreementHandler->detachAgreement(peer, tid);
+ invalidateActions(peer, tid, originator);
+ if (agreement) {
+ sendUp(recipientDataService->retireAgreement(agreement, associationReset));
+ emit(blockAckAgreementDeletedSignal, agreement);
+ delete agreement;
+ }
+ }
+ scheduleInactivityTimer();
+}
+
+void Hcf::beginTeardown(MacAddress peer, Tid tid, bool originator, int reasonCode)
+{
+ if (!originatorBlockAckAgreementHandler || !recipientBlockAckAgreementHandler)
+ return;
+ if (originator) {
+ const auto& procedures = originatorBlockAckAgreementHandler->getProcedures();
+ auto it = procedures.find({peer, tid});
+ if (it != procedures.end() && it->second.state == IOriginatorBlockAckAgreementHandler::ProcedureState::TEARDOWN_PENDING)
+ return;
+ originatorBlockAckAgreementHandler->beginTeardown(peer, tid);
+ }
+ else {
+ if (recipientBlockAckAgreementHandler->isTeardownPending(peer, tid))
+ return;
+ recipientBlockAckAgreementHandler->beginTeardown(peer, tid);
+ }
+ retireAgreement(peer, tid, originator);
+ auto delba = makeShared();
+ delba->setReceiverAddress(peer);
+ delba->setTid(tid);
+ delba->setInitiator(originator);
+ delba->setReasonCode(reasonCode);
+ processMgmtFrame(new Packet("Delba", delba), delba);
+ scheduleAddbaResponseTimer();
+}
+
+void Hcf::actionTerminated(Packet *packet, bool acknowledged)
+{
+ auto it = ownedActions.find(packet->getId());
+ if (it == ownedActions.end())
+ return;
+ auto action = it->second;
+ ownedActions.erase(it);
+ if (action.rtsRecoveryOwned)
+ edca->getEdcaf(AC_VO)->getMgmtAndNonQoSRecoveryProcedure()->dropRtsProtectedFrame(packet->peekAtFront());
+ if (action.kind == OwnedAction::REQUEST) {
+ auto agreement = originatorBlockAckAgreementHandler->getAgreement(action.peer, action.tid);
+ if (!agreement || agreement->getIsAddbaResponseReceived())
+ return;
+ if (acknowledged)
+ originatorBlockAckAgreementHandler->acknowledgeRequest(action.peer, action.tid,
+ originatorBlockAckAgreementPolicy->computeAddbaFailureTimeout());
+ else if (action.attempted)
+ // INET recovery policy confirms peer teardown before another setup.
+ beginTeardown(action.peer, action.tid, true, 37);
+ else {
+ originatorBlockAckAgreementHandler->finishProcedure(action.peer, action.tid);
+ retireAgreement(action.peer, action.tid, true);
+ }
+ }
+ else if (action.kind == OwnedAction::DELBA) {
+ if (action.originator) {
+ if (acknowledged)
+ originatorBlockAckAgreementHandler->finishProcedure(action.peer, action.tid);
+ else {
+ // INET recovery policy discards this batch after failed DELBA delivery.
+ originatorBlockAckAgreementHandler->failTeardown(action.peer, action.tid);
+ discardAgreementData(action.peer, action.tid, true);
+ }
+ }
+ else
+ recipientBlockAckAgreementHandler->finishTeardown(action.peer, action.tid);
+ }
+ scheduleAddbaResponseTimer();
+ if (action.kind == OwnedAction::DELBA || (action.kind == OwnedAction::REQUEST && !acknowledged)) {
+ updateAgreementProcedures();
+ updateContention();
+ }
+}
+
+void Hcf::resetPeer(MacAddress peer)
+{
+ clearInterframeHistory();
+ Enter_Method("resetPeer");
+ if (!originatorBlockAckAgreementHandler)
+ return;
+ bool previous = handlingAgreementAction;
+ handlingAgreementAction = true;
+ auto originators = originatorBlockAckAgreementHandler->detachPeer(peer);
+ auto recipients = recipientBlockAckAgreementHandler->detachPeer(peer);
+ for (int tid = 0; tid < 16; tid++) {
+ invalidateActions(peer, tid, true);
+ invalidateActions(peer, tid, false);
+ discardAgreementData(peer, tid, true);
+ }
+ for (auto agreement : recipients)
+ sendUp(recipientDataService->retireAgreement(agreement, true));
+ recipientDataService->clearPeer(peer);
+ for (auto agreement : originators) {
+ if (agreement->getIsAddbaResponseReceived())
+ emit(blockAckAgreementDeletedSignal, agreement);
+ delete agreement;
+ }
+ for (auto agreement : recipients) {
+ emit(blockAckAgreementDeletedSignal, agreement);
+ delete agreement;
+ }
+ handlingAgreementAction = previous;
+ scheduleInactivityTimer();
+ scheduleAddbaResponseTimer();
+}
+
+void Hcf::receiveSignal(cComponent *source, simsignal_t signalID, bool value, cObject *details)
+{
+ if (signalID != Ieee80211Mib::rateStateChangedSignal)
+ return;
+ Enter_Method("rateStateChanged");
+ // This cleanup prepares Compressed support. Basic agreements do not depend on HT admission state.
+ if (blockAckVariant == BlockAckVariant::BASIC || !originatorBlockAckAgreementHandler)
+ return;
+ std::vector> retired;
+ for (const auto& entry : originatorBlockAckAgreementHandler->getAgreements()) {
+ if (!isProfileAllowed(entry.first.first, true))
+ retired.push_back({entry.first.first, entry.first.second, true});
+ }
+ for (const auto& entry : recipientBlockAckAgreementHandler->getAgreements()) {
+ if (!isProfileAllowed(entry.first.first, false))
+ retired.push_back({entry.first.first, entry.first.second, false});
+ }
+ for (const auto& entry : retired)
+ beginTeardown(std::get<0>(entry), std::get<1>(entry), std::get<2>(entry), 37);
+ updateContention();
}
void Hcf::processLowerFrame(Packet *packet, const Ptr& header)
{
Enter_Method("processLowerFrame(%s)", packet->getName());
take(packet);
+ if (auto bar = dynamicPtrCast(header)) {
+ if (!validateBlockAckRequest(packet, bar)) {
+ interframeHistory = {};
+ if (auto time = packet->findTag())
+ pendingReceptions.erase(time->getTransmissionId());
+ delete packet;
+ return;
+ }
+ }
+ bool interframe = interframeRadio && receiveInterframe(packet, header);
EV_INFO << "Processing lower frame: " << packet->getName() << endl;
auto edcaf = edca->getChannelOwner();
+ auto action = dynamicPtrCast(header);
+ bool blockAckAction = dynamicPtrCast(header) ||
+ dynamicPtrCast(header) || dynamicPtrCast(header);
+ if (action && blockAckAction && isForUs(header) && edcaf && frameSequenceHandler->isSequenceRunning() &&
+ dynamic_cast(frameSequenceHandler->getContext()->getLastStep())) {
+ handlingAgreementAction = true;
+ emit(packetReceivedFromPeerSignal, packet);
+ std::unique_ptr responseSource(packet->dup());
+ auto accepted = recipientDataService->managementFrameReceived(packet, action);
+ for (auto frame : accepted) {
+ take(frame);
+ dispatchAgreementAction(frame->peekAtFront());
+ }
+ frameSequenceHandler->interruptFrameSequence();
+ recipientAckProcedure->processReceivedFrame(responseSource.get(), action, check_and_cast(recipientAckPolicy), this);
+ for (auto frame : accepted)
+ delete frame;
+ handlingAgreementAction = false;
+ updateAgreementProcedures();
+ updateContention();
+ return;
+ }
if (edcaf && frameSequenceHandler->isSequenceRunning()) {
// TODO always call processResponse?
if ((!isForUs(header) && !startRxTimer->isScheduled()) || isForUs(header)) {
@@ -323,7 +1038,7 @@ void Hcf::processLowerFrame(Packet *packet, const Ptr