Describe the Bug
VALID_TRANSITIONS in packages/toolkit/src/core/detection.ts does not match the
status transition table in OCPP 1.6 edition 2, section 4.9. It disagrees in both
directions: it flags 22 transitions the table permits, and it permits 2 the table
does not list.
The table in section 4.9 lists 53 permitted transitions across the nine
ChargePointStatus values. Written out per source status:
| From |
Permitted target statuses (section 4.9) |
| Available |
Preparing, Charging, SuspendedEV, SuspendedEVSE, Reserved, Unavailable, Faulted |
| Preparing |
Available, Charging, SuspendedEV, SuspendedEVSE, Finishing, Faulted |
| Charging |
Available, SuspendedEV, SuspendedEVSE, Finishing, Unavailable, Faulted |
| SuspendedEV |
Available, Charging, SuspendedEVSE, Finishing, Unavailable, Faulted |
| SuspendedEVSE |
Available, Charging, SuspendedEV, Finishing, Unavailable, Faulted |
| Finishing |
Available, Preparing, Unavailable, Faulted |
| Reserved |
Available, Preparing, Unavailable, Faulted |
| Unavailable |
Available, Preparing, Charging, SuspendedEV, SuspendedEVSE, Faulted |
| Faulted |
Available, Preparing, Charging, SuspendedEV, SuspendedEVSE, Finishing, Reserved, Unavailable |
The rule's matrix has 33 entries, of which 31 are in the table.
Flagged although the table permits them (false positives), 22:
Available -> SuspendedEV Available -> SuspendedEVSE
Preparing -> SuspendedEV Preparing -> Finishing
Charging -> Unavailable
SuspendedEV -> SuspendedEVSE SuspendedEV -> Unavailable
SuspendedEVSE -> SuspendedEV SuspendedEVSE -> Unavailable
Finishing -> Preparing Finishing -> Unavailable
Reserved -> Preparing
Unavailable -> Preparing Unavailable -> Charging
Unavailable -> SuspendedEV Unavailable -> SuspendedEVSE
Faulted -> Preparing Faulted -> Charging
Faulted -> SuspendedEV Faulted -> SuspendedEVSE
Faulted -> Finishing Faulted -> Reserved
The Faulted and Unavailable rows are the worst of it. The table's I1-I8
row means a connector recovering from a fault may return to any pre-fault state,
and the H row means a connector coming back from Unavailable may go straight
to any operative state. The rule allows recovery only to Available, so every
station that faults mid-session and resumes charging, or that comes off a
ChangeAvailability back into a live session, produces a spurious warning.
Charging -> Unavailable (C8) and the three -> Unavailable rows are the same
kind of thing: a scheduled availability change lands during a session and gets
flagged.
Permitted although the table does not list them (false negatives), 2:
Preparing -> Unavailable (no B8 cell)
Finishing -> Reserved (no F7 cell)
Steps to Reproduce
Build the toolkit, then evaluate a two-message StatusNotification trace for each
ordered pair and compare against the table. For a single case:
const events = [
{ message: [2, 'n1', 'StatusNotification', { connectorId: 1, status: 'Faulted' }] },
{ message: [2, 'n2', 'StatusNotification', { connectorId: 1, status: 'Charging' }] },
];
const trace = parseTrace(JSON.stringify({ events }));
detectFailures(trace.events, buildSessionTimeline(trace.events)).map((f) => f.code);
Expected Behavior
Faulted -> Charging is cell I3 in the table, so no violation.
Actual Behavior
STATUS_TRANSITION_VIOLATION.
Environment
- OCPP DebugKit version: 0.4.4
- Node.js version: 26.x
- OS: macOS
Proposed Fix
Replace VALID_TRANSITIONS with the table above, transcribed cell by cell, and
cite section 4.9 next to it so the next reader can check it against the spec
rather than against intuition. Add tests for the recovery rows (Faulted -> and
Unavailable ->) and for the two transitions that should now be flagged.
Two adjacent questions this issue deliberately does not change, so that the fix
stays a straight transcription. Both are worth their own issue if we want them:
- Repeated identical status. The table has no diagonal, so the rule treats
Available -> Available as a violation, and will continue to. A charge point
answering a TriggerMessage for StatusNotification, or re-reporting state
after a reconnect, legitimately repeats its current status. Worth deciding
whether a repeat should be exempt.
- connectorId 0. Section 4.9 notes the table applies to
connectorId > 0,
and that connectorId 0 is limited to Available, Unavailable, and Faulted.
Every ordered pair among those three is permitted by the full table, so
applying one matrix to both produces no false positives, but it also means a
connectorId 0 reporting Charging is not flagged as out of its applicable set.
Note for whoever picks this up: DebugKit Studio carries an independent
implementation of this rule with the same matrix (src/ocpp/detection.zig), held
against the toolkit by the contract-v1 conformance corpus. It needs the same
transcription, or the two implementations diverge on every pair the shared
corpus does not cover.
Describe the Bug
VALID_TRANSITIONSinpackages/toolkit/src/core/detection.tsdoes not match thestatus transition table in OCPP 1.6 edition 2, section 4.9. It disagrees in both
directions: it flags 22 transitions the table permits, and it permits 2 the table
does not list.
The table in section 4.9 lists 53 permitted transitions across the nine
ChargePointStatusvalues. Written out per source status:The rule's matrix has 33 entries, of which 31 are in the table.
Flagged although the table permits them (false positives), 22:
The
FaultedandUnavailablerows are the worst of it. The table'sI1-I8row means a connector recovering from a fault may return to any pre-fault state,
and the
Hrow means a connector coming back fromUnavailablemay go straightto any operative state. The rule allows recovery only to
Available, so everystation that faults mid-session and resumes charging, or that comes off a
ChangeAvailabilityback into a live session, produces a spurious warning.Charging -> Unavailable(C8) and the three-> Unavailablerows are the samekind of thing: a scheduled availability change lands during a session and gets
flagged.
Permitted although the table does not list them (false negatives), 2:
Steps to Reproduce
Build the toolkit, then evaluate a two-message
StatusNotificationtrace for eachordered pair and compare against the table. For a single case:
Expected Behavior
Faulted -> Chargingis cellI3in the table, so no violation.Actual Behavior
STATUS_TRANSITION_VIOLATION.Environment
Proposed Fix
Replace
VALID_TRANSITIONSwith the table above, transcribed cell by cell, andcite section 4.9 next to it so the next reader can check it against the spec
rather than against intuition. Add tests for the recovery rows (
Faulted ->andUnavailable ->) and for the two transitions that should now be flagged.Two adjacent questions this issue deliberately does not change, so that the fix
stays a straight transcription. Both are worth their own issue if we want them:
Available -> Availableas a violation, and will continue to. A charge pointanswering a
TriggerMessageforStatusNotification, or re-reporting stateafter a reconnect, legitimately repeats its current status. Worth deciding
whether a repeat should be exempt.
connectorId > 0,and that connectorId 0 is limited to
Available,Unavailable, andFaulted.Every ordered pair among those three is permitted by the full table, so
applying one matrix to both produces no false positives, but it also means a
connectorId 0 reporting
Chargingis not flagged as out of its applicable set.Note for whoever picks this up:
DebugKit Studiocarries an independentimplementation of this rule with the same matrix (
src/ocpp/detection.zig), heldagainst the toolkit by the
contract-v1conformance corpus. It needs the sametranscription, or the two implementations diverge on every pair the shared
corpus does not cover.