Thanks @RealSpap. A second implementation written from the text, rather than from my code, is the confirmation I was hoping for.
On your question: yes, for the case you describe. Under the #2077 wording, a renewal counts only if it falls between the `TrustSet` and the end of the grace period in force at issuance, so a later registrant’s renewal never lifts the bound. As you say, though, your cases don’t test the other side, and that wording has a gap there: a single renewal in that window lifts the bound for good. If the holder renews once, later lets the name lapse, and someone else registers it, the path passes. On a mainnet fork, G→X→M2 passed after X was renewed once, lapsed and was registered by someone else.
I’ve proposed replacing the rule in #2080. It is still a draft and changes only the spec text and tests; the contract and `0x94f0ee34` are unchanged. The replacement is a registration boundary: an attestation becomes ineligible once the BaseRegistrar emits `NameRegistered` for the label of a `.eth` second-level name on it, as trustor or trustee, after its `TrustSet`. `NameRenewed` never moves the boundary, so the question of which renewals count goes away. The same check now applies to any name on the path below a `.eth` second-level name, since the ancestor’s next registrant can reassign it. Ordering adds the transaction index. A coordinator that can’t establish the label, or the full event history up to its decision, doesn’t rely on the attestation.
Everything you observed is also the intended result under the new rule:
- re-registered paths are rejected, with or without a cap;
- nothing is rejected at issuance or after a renewal;
- the same holder re-registering is rejected deliberately, because the rule tracks registration rather than control. Every trustor that endorsed the name then signs again, not only the holder for its own endorsements; the text now says so.
The `controllerOf` case is the reason the text keeps two checks. Continuity asks whether anyone has registered the name again; liveness asks whether it has a controller now. A renewal that calls the BaseRegistrar directly passes continuity but can fail liveness once the old wrapper expiry passes.
If you have time, the run I’d find most useful against #2080 is the one your set leaves out. Renew M (or T) once within grace, then let it lapse and have someone else register it. Under the #2077 reading, that path passes; under the boundary rule, it should fail. Several renewals with no re-registration should still pass. #2080 stays in draft until then.