# 모듈 전체를 돌리면
MutualTlsHandshakeContractTest > anUntrustedAuthorityIsAPermanentTlsFailure() FAILED
    org.opentest4j.AssertionFailedError at MutualTlsHandshakeContractTest.java:168
MutualTlsHandshakeContractTest > anExpiredCertificateIsAPermanentTlsFailure() FAILED
    org.opentest4j.AssertionFailedError at MutualTlsHandshakeContractTest.java:168
MutualTlsHandshakeContractTest > aHostnameMismatchIsAPermanentTlsFailure() FAILED
    org.opentest4j.AssertionFailedError at MutualTlsHandshakeContractTest.java:168
283 tests completed, 3 failed
> Task :adapter:outbound:httpclient:test FAILED

# 멈추는 자리는 두 번째 단언이다
164:  private void assertPermanent(Throwable captured) {
165:    TransportFailure classified =
166:        new ApacheFailureClassifier().classify(captured, AttemptStage.TLS_HANDSHAKE);
167:    assertThat(classified.evidence()).isEqualTo(ExecutionEvidence.NOT_SENT);
168:    assertThat(classified.stage()).isEqualTo(AttemptStage.TLS_HANDSHAKE);
169:    assertThat(classified.category()).isEqualTo(FailureCategory.TLS_PERMANENT);
170:    assertThat(classified.category().permanent()).isTrue();
171:  }
  (167 통과 · 168 실패 · 169 이하 미실행)

# 픽스처가 돌려주는 것은 호스트명이다
70:  public URI uri(String path) {
71-    return server.url(path).uri();
72-  }

# 이 컨테이너에서 그 호스트명이 푸는 주소
1:127.0.0.1	localhost
2:::1	localhost ip6-localhost ip6-loopback
  localhost/127.0.0.1
  localhost/0:0:0:0:0:0:0:1
# src 아래의 preferIPv4Stack / preferIPv6Addresses 설정: 0

# 강등이 재시도 판정에 닿는 지점
  /** Permanent failures are never retried regardless of evidence, deadline, or budget. */
  public boolean permanent() {
    return this == CONFIGURATION
        || this == TARGET_REJECTED
        || this == TLS_PERMANENT
        || this == SERIALIZATION
        || this == RESPONSE_TOO_LARGE
        || this == REDIRECT_REJECTED
        || this == DEADLINE_EXCEEDED
        || this == CANCELLED;
  }
  엔진의 가드 순서
    if (context.attempt() >= context.maxAttempts()) {
      return RetryDenied.maxAttempts();
    }
    if (!context.budget().available()) {
      return RetryDenied.budgetExhausted();
    if (context.failureCategory().permanent()) {
      return RetryDenied.permanentFailure(context.failureCategory().name());
    }
      case CONNECT -> RetryAllowed.of("CONNECT");
