--- /SHiREVault/Backup/OSBackups/SHIRE-ACADEMY-ROUND3-AST-SOURCE-REPAIR-20260805T122334Z/before/design_academy_practice_service.py 2026-08-05 20:23:33.781444300 +0800 +++ /SHiREVault/Backup/OSBackups/SHIRE-ACADEMY-ROUND3-AST-SOURCE-REPAIR-20260805T122334Z/staged/design_academy_practice_service.py 2026-08-05 20:23:34.788427200 +0800 @@ -702,22 +702,64 @@ @staticmethod def _transient_brain_failure(error: Exception) -> bool: + """ + Treat only transport, timeout and local service availability failures + as infrastructure. Contract and learner-content errors are semantic. + """ + payload = ( + error.payload + if isinstance(error, AcademyBrainResponseError) + and isinstance(error.payload, dict) + else {} + ) + + if ( + payload.get("content_format_failure") is True + or payload.get("failure_class") + == "academy_output_contract_failure" + or payload.get("infrastructure_failure") is False + ): + return False + + if payload.get("infrastructure_failure") is True: + return payload.get("retryable") is True + + payload_error = str( + payload.get("error") or "" + ).lower() + + if any( + token in payload_error + for token in ( + "ollama http error", + "ollama unavailable", + "service unavailable", + "out of memory", + "oom", + "process was killed", + ) + ): + return True + text = str(error).lower() + return any( token in text for token in ( + "brain api practice request failed", "timed out", "timeout", "connection refused", "temporarily unavailable", + "service unavailable", "remote end closed", "connection reset", - "academy structured output invalid", - "round-specific canonicalisation", - "canonical json gate", - "response contract guard", - "http 502", - "http 504", + "connection aborted", + "broken pipe", + "name or service not known", + "network is unreachable", + "out of memory", + "process was killed", ) )