Good First Issue
Add a scenario status-transitions-legal. It is a negative control: a connector
walking a completely legal status path must produce no failures at all.
This is the highest-value gap in the corpus. STATUS_TRANSITION_VIOLATION has been
wrong twice, in #128 and again in #155, and #155 found the rule was flagging 22
transitions that the OCPP 1.6 table actually permits. Nothing in the corpus would
have caught that. This scenario is that guard.
What to do
- Create
packages/toolkit/src/scenarios/__scenarios__/status-transitions-legal.ts
- Build a synthetic trace that walks a legal path and nothing else:
BootNotification at 2026-02-01T10:00:00.000Z, and its CallResult with
interval: 300
StatusNotification on connector 1 with errorCode: 'NoError' at each of
these times and statuses, each with its own CallResult:
10:00:30.000Z Available, 10:01:00.000Z Preparing,
10:02:00.000Z Charging, 10:03:00.000Z Finishing,
10:04:00.000Z Available
Heartbeat at 10:05:00.000Z, and its CallResult
- Set
expectedFailures: []
- Add
{ type: 'no_failures', params: {} } as the assertion
- Import and register in
packages/toolkit/src/scenarios/index.ts
- Update the scenario count in every place that states it, see "Files to modify"
- Run
ocpp-debugkit ci, then the full local check from
Verify Locally.
ocpp-debugkit ci covers neither formatting nor lint nor types, and CI stops at
the first failure.
- Add a changeset with
pnpm changeset, and pick patch. See
How to Add a Scenario.
Why those statuses
Every step is a transition the section 4.9 table permits:
Available -> Preparing -> Charging -> Finishing -> Available. The trailing
Heartbeat keeps the heartbeat rules quiet, and the whole trace finishes inside
twice the 300 second interval so TIMEOUT_NO_HEARTBEAT stays silent too. This
trace was run against the detection engine and reports no failures.
Files to modify
packages/toolkit/src/scenarios/__scenarios__/status-transitions-legal.ts (new)
packages/toolkit/src/scenarios/index.ts (register, and add to scenarioNames)
packages/toolkit/src/scenarios/index.test.ts (count, name order, getScenario)
tests/external-fixture/test.mjs (count)
README.md (count)
packages/toolkit/README.md (count)
.changeset/ (new changeset file)
Only the first two of the count sites fail the suite when they drift, so the two
READMEs are easy to miss. Do not touch the detection-rule count, which is a
different number and stays at 16.
Guidelines
- All data must be synthetic. This scenario reserves station ID
CS-SYNTHETIC-022, so please use that one, and number the idTag to match if
you add one.
meter-value-zero.ts is the closest existing negative control to copy the shape
from.
- Verify with
ocpp-debugkit scenario run status-transitions-legal that no
failures are reported. If a rule does fire, fix the trace rather than adding the
code to expectedFailures. Staying clean is the entire point of this scenario.
- The scenario count depends on what has merged ahead of you. Run the suite and use
the number it reports rather than assuming.
- Branch off current
main, not off another scenario branch.
How to claim
Comment "I'd like to work on this" and it will be assigned to you.
Please hold one open claim at a time. Once the pull request for it is merged, say
which issue you want next and it will be assigned.
Good First Issue
Add a scenario
status-transitions-legal. It is a negative control: a connectorwalking a completely legal status path must produce no failures at all.
This is the highest-value gap in the corpus.
STATUS_TRANSITION_VIOLATIONhas beenwrong twice, in #128 and again in #155, and #155 found the rule was flagging 22
transitions that the OCPP 1.6 table actually permits. Nothing in the corpus would
have caught that. This scenario is that guard.
What to do
packages/toolkit/src/scenarios/__scenarios__/status-transitions-legal.tsBootNotificationat2026-02-01T10:00:00.000Z, and itsCallResultwithinterval: 300StatusNotificationon connector 1 witherrorCode: 'NoError'at each ofthese times and statuses, each with its own
CallResult:10:00:30.000ZAvailable,10:01:00.000ZPreparing,10:02:00.000ZCharging,10:03:00.000ZFinishing,10:04:00.000ZAvailableHeartbeatat10:05:00.000Z, and itsCallResultexpectedFailures: []{ type: 'no_failures', params: {} }as the assertionpackages/toolkit/src/scenarios/index.tsocpp-debugkit ci, then the full local check fromVerify Locally.
ocpp-debugkit cicovers neither formatting nor lint nor types, and CI stops atthe first failure.
pnpm changeset, and pick patch. SeeHow to Add a Scenario.
Why those statuses
Every step is a transition the section 4.9 table permits:
Available -> Preparing -> Charging -> Finishing -> Available. The trailingHeartbeatkeeps the heartbeat rules quiet, and the whole trace finishes insidetwice the 300 second interval so
TIMEOUT_NO_HEARTBEATstays silent too. Thistrace was run against the detection engine and reports no failures.
Files to modify
packages/toolkit/src/scenarios/__scenarios__/status-transitions-legal.ts(new)packages/toolkit/src/scenarios/index.ts(register, and add toscenarioNames)packages/toolkit/src/scenarios/index.test.ts(count, name order,getScenario)tests/external-fixture/test.mjs(count)README.md(count)packages/toolkit/README.md(count).changeset/(new changeset file)Only the first two of the count sites fail the suite when they drift, so the two
READMEs are easy to miss. Do not touch the detection-rule count, which is a
different number and stays at 16.
Guidelines
CS-SYNTHETIC-022, so please use that one, and number theidTagto match ifyou add one.
meter-value-zero.tsis the closest existing negative control to copy the shapefrom.
ocpp-debugkit scenario run status-transitions-legalthat nofailures are reported. If a rule does fire, fix the trace rather than adding the
code to
expectedFailures. Staying clean is the entire point of this scenario.the number it reports rather than assuming.
main, not off another scenario branch.How to claim
Comment "I'd like to work on this" and it will be assigned to you.
Please hold one open claim at a time. Once the pull request for it is merged, say
which issue you want next and it will be assigned.