needsNewUpload property

bool? needsNewUpload
final

Whether this build can only be fixed by uploading another one, or null when that cannot be said.

usable alone hides the question an operator actually has, and this field exists because that cost a consumer a real defect: it built advice on usable == false and told an operator to wait for VALID in every case. That is right for PROCESSING and wrong for FAILED and INVALID, which are Apple refusing the binary and never change again — so the advice was "wait forever" for the two states where the answer is "upload a different build".

Phrased as the action, and readable on its own. An earlier draft called this mayBecomeUsable, which is false for a perfectly healthy VALID build — and false there reads as "give up" to anyone who has not also read usable first. A pair that is only safe in one reading order gets read in the other one, which is precisely how the defect above happened. This one is correct alone in every state.

False while Apple is still processing and for a usable build, true once Apple has refused the binary and for an expired build, including one that processed cleanly — expiry is terminal reached from a healthy state, and the older phrasing gave it the same answer as a healthy build. Null for a state this version does not name.

A refused build and an expired one are the same answer here, and expired is what tells them apart. Both are usable: false, needsNewUpload: true, because the next action genuinely is the same — upload another. What differs is what a human should be told: rejected and expired are not the same sentence, and this pair alone cannot say which. Read expired when the wording matters.

Implementation

final bool? needsNewUpload;