needsNewUpload property
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;