I should have posted this bug when I first noticed the problem, but mail-filter/spamassassin-3.1.3 (and 3.1.2, also) breaks the --exit-code option. With 3.1.0, the option works as expected; that is, when it checks a piece of mail that is nonspam, it returns 0, but when it checks a piece of mail that _is_ spam, it returns some failure (5 by default). With 3.1.[23], spamassassin returns 0 every time, indicating that every mail is nonspam. It looked like $exitvalue wasn't being propagated out of sub wanted{}. A print statement inside the eval block on line 342 shows that $exitvalue never changes in the new version, even if it changes in the wanted{} function.
Upstream is already aware of the problem and they have issued a patch. http://issues.apache.org/SpamAssassin/show_bug.cgi?id=4930 This corrected the issue for me.
In cvs (spamassassin-3.1.3-r1). Didn't patch older versions as they will be removed after all arches are marked stable for version 3.1.3. See bug #135746 for further reference.