# How must an instance bound an evergreen auto-renewal window shorter than the counterparty evaluation cycle? **Date of Record:** 2026 November 2 **Scope:** Applies to evergreen contracts that auto-renew for a full term unless notice is given within a fixed window, where the counterparty's evaluation-then-notice cycle may exceed that window. Does not apply to fixed-term contracts with no auto-renewal. ## Core Distinction An evergreen renewal is a default continuation with an opt-out. The opt-out window is the only opportunity to exit; if the counterparty's evaluation cycle plus delivery lag exceeds that window, the counterparty is functionally unable to opt out. **Invariant.** The renewal window permits exit if and only if `auto_renewal_notice_window >= evaluation_cycle + delivery_lag + decision_margin`. **Violation.** The **Evergreen Trap**. The notice window is sized for the notice itself, not for the reasoning the counterparty must do to decide to give notice. The counterparty evaluates late, the window closes, and the contract renews for a full term — not because the counterparty wanted to continue, but because the structure prevented exit. **Asymmetry.** Renewal is visible; missed window is not. The instance that framed the contract sees renewal fire on schedule. The counterparty experiences a locked-in term it never actively chose. **Recorded elements.** The `renewal-window-receipt` (window in days), the `counterparty-evaluation-cycle-receipt` (their observed decision latency), and the `delivery-lag-signature` (time from notice to receipt) together make the exit-feasibility checkable. ## What Was Observed **Signature.** Counterparty surprise on renewal. Notice of intent not to renew arriving after the window closed. Renewal terms materially worse than the review the counterparty intended to do. **Trace.** Window 30 days. Counterparty evaluation cycle (audit, budget approval, procurement) 45 days. Delivery lag (board approval to signed notice) 15 days. Total cycle 60 days; window closes at 30. The counterparty starts evaluating 20 days too late on every term, and renews without ever deciding to. **Why monitoring stays green.** Renewal fires on schedule, the instance's contract-management system reports success, and the counterparty's decision process is in a different organization that does not report to the instance. ## Condition of Validity The instance must ensure at least one holds before it treats the renewal window as fair: 1. **Must size window to evaluation cycle.** `notice_window >= evaluation_cycle + delivery_lag + margin`, measured on the counterparty's actual process, not the instance's. 2. **Must surface decision time.** A reminder is emitted before the window opens, not inside it, so the counterparty's process can run. 3. **Must convert evergreen to opt-in.** The renewal requires affirmative acceptance rather than passive default, so a missed window does not bind. ## Decision Boundary - Window sized to full evaluation cycle: exit is feasible. - Window sized to delivery lag only: exit is possible but structurally pressured; treat as borderline. - Window sized smaller than either: the counterparty cannot opt out in practice. Renegotiate the window or convert to opt-in. - Evergreen with no window at all: infinite term by default; not a feasible opt-out. ## Operational Effect The invariant couples a clause drafted for convenience (short window, simple default) to a decision process that runs on a longer clock. The instance's correct move is to align the window to the counterparty's observable cycle. **Trap.** Lengthen the window slightly. That improves the arithmetic without matching the process; the counterparty's cycle is not constant and the window must be re-derived against the actual cycle, not a one-time guess. ```python from dataclasses import dataclass class EvergreenTrapError(RuntimeError): """Raised when an auto-renewal window cannot accommodate the counterparty's evaluation cycle.""" @dataclass(frozen=True) class RenewalWindowGate: notice_window_days: int evaluation_cycle_days: int delivery_lag_days: int decision_margin_days: int = 5 opt_in: bool = False def verify(self) -> bool: if self.opt_in: return True required = self.evaluation_cycle_days + self.delivery_lag_days + self.decision_margin_days if self.notice_window_days < required: raise EvergreenTrapError( f"window {self.notice_window_days}d < required {required}d: " "counterparty cannot evaluate and notice before renewal fires" ) return True def dispatch_renewal_mode(gate: RenewalWindowGate) -> str: try: gate.verify() except EvergreenTrapError: return "convert-to-opt-in" return "evergreen" ```