From the documentation...
Use the COND parameter to test return codes from previous job steps
and determine whether to bypass this job step. You can specify one or
more tests on the COND parameter, and you can test return codes from
particular job steps or from every job step that has completed
processing. If any of the test conditions are satisfied, the system
evaluates the COND parameter as true and bypasses the job step. If
none of the test conditions specified on the COND parameter are
satisfied, the system evaluates the COND parameter as false and
executes the job step.
The system performs the COND parameter tests against return codes from
the current execution of the job. If a test returns a previously
bypassed step, the system evaluates the test as false.
Note the text I have italicized. Because "the system evaluates the test as false" the job step is executed. In other words, the COND parameter of the EXEC statement is working as documented. As to why the parameter was designed to work this way, I'm afraid I wasn't working in this field when it was invented some 40+ years ago and thus cannot speak to the reasoning of the original developers.
You might want to look into the IF statement, which provides the capability of testing if a given step has run in addition to testing its return code.
//STEP01 EXEC
//CK01 IF STEP1.RC EQ 0 THEN
//STEP02 EXEC
//CK01E ENDIF
//CK02 IF STEP2.RUN & STEP2.RC EQ 0
//STEP03 EXEC
//CK02E ENDIF
alternatively, you could code...
//STEP01 EXEC
//CK01 IF STEP1.RC EQ 0 THEN
//STEP02 EXEC
//CK02 IF STEP2.RC EQ 0
//STEP03 EXEC
//CK02E ENDIF
//CK01E ENDIF