It's not easy for pyforgejo to do much about this, I don't think, but I figured it's at least worth noting.
I think because the forgejo API doesn't use required to indicate which response keywords we can rely upon to exist, pyforgejo winds up using typing.Optional for every property of every response object. This means that, technically, you can't rely on anything not being None, and type checkers will complain if you do, like this:
src/ai_code_review/core/forgejo_client.py:109: error: Argument "id" to "PullRequestInfo" has incompatible type "int | None"; expected "int" [arg-type]
src/ai_code_review/core/forgejo_client.py:110: error: Argument "number" to "PullRequestInfo" has incompatible type "int | None"; expected "int" [arg-type]
src/ai_code_review/core/forgejo_client.py:111: error: Argument "title" to "PullRequestInfo" has incompatible type "str | None"; expected "str" [arg-type]
src/ai_code_review/core/forgejo_client.py:113: error: Item "None" of "PrBranchInfo | None" has no attribute "ref" [union-attr]
src/ai_code_review/core/forgejo_client.py:113: error: Argument "source_branch" to "PullRequestInfo" has incompatible type "str | Any | None"; expected "str" [arg-type]
src/ai_code_review/core/forgejo_client.py:114: error: Item "None" of "PrBranchInfo | None" has no attribute "ref" [union-attr]
src/ai_code_review/core/forgejo_client.py:114: error: Argument "target_branch" to "PullRequestInfo" has incompatible type "str | Any | None"; expected "str" [arg-type]
src/ai_code_review/core/forgejo_client.py:115: error: Item "None" of "User | None" has no attribute "login" [union-attr]
src/ai_code_review/core/forgejo_client.py:115: error: Argument "author" to "PullRequestInfo" has incompatible type "str | Any | None"; expected "str" [arg-type]
src/ai_code_review/core/forgejo_client.py:116: error: Argument "state" to "PullRequestInfo" has incompatible type "str | None"; expected "str" [arg-type]
src/ai_code_review/core/forgejo_client.py:117: error: Argument "web_url" to "PullRequestInfo" has incompatible type "str | None"; expected "str" [arg-type]
these are all because the types in pyforgejo's PullRequest model (where I'm getting the values from) are typing.Optional[int], typing.Optional[str] etc. Making all your code (somewhat nonsensically) defend against things like the pull request number potentially being None is a bit of a drag.
Specifically I think this happens because there's no real way to indicate in a Python class that it may or may not have a given property with particular attributes, so instead fern gives properties that aren't required in the API definition a default value of None and makes them typing.Optional, but I'm guessing a bit there, fern is a huge codebase and it's hard to find the bit that does this.
It's also worth noting that forgejo's API definition does not indicate which response properties are nullable. Technically it's saying that none of them are nullable; a literal reading of the API definition is something like "you can't rely on any of these properties being in the response at all, but if they are, none of them will be null". Which is obviously not true. The real situation seems to be more like "you can always rely on all the properties being in the response, but some of them might be null".