Rule “System reached start level 100” can be executed multiple times. Why?

OH: 4.3.0-4.3.5
JAVA: Zulu v17.58.21 CA-JDK v17.0.15 x64
OS: Windows Server 2022 Std Latest
HW: ASUSPN41/IntelPentiumSilverN6000@2100/8GB_DDR4-2666/256GB_SSD-WD-GREEN
JS: application/javascript ECMAScript 262 Edition 11
Experience: 3+ years with OH v3&4

Is the system behaving correctly: when I change some items (via ui) or edit rules in files and save the changes, my rule with “System reached start level 100” can be executed.
I’m not editing the file itself, where the message is output to the log.
I have other rules that need to be executed once at system startup. I don’t want them to be executed multiple times.

Is this correct behavior?
I thought that “System reached start level 100” is executed only once.

rule "OpenHAB: Startup"
when
  System reached start level 100
then  
  val _message = "🏁 OpenHAB ready.";
  logInfo("openhab_startup", _message);
...

Example of log when editing rules:

2025-05-14 15:18:59.544 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'openhab.rules'
2025-05-14 15:18:59.848 [INFO ] [ab.core.model.script.openhab_startup] - 🏁 OpenHAB ready.
2025-05-14 15:31:22.743 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'openhab.rules'
2025-05-14 15:31:23.284 [INFO ] [ab.core.model.script.openhab_startup] - 🏁 OpenHAB ready.
2025-05-14 15:31:40.287 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'openhab.rules'
2025-05-14 15:31:40.715 [INFO ] [ab.core.model.script.openhab_startup] - 🏁 OpenHAB ready.
2025-05-14 15:32:35.910 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'openhab.rules'
2025-05-14 15:32:36.440 [INFO ] [ab.core.model.script.openhab_startup] - 🏁 OpenHAB ready.
2025-05-14 15:33:55.479 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'intrusion.rules'
2025-05-14 15:34:29.556 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'network.rules'
2025-05-14 15:34:48.358 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'rflnk.rules'
2025-05-14 15:35:07.832 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'alarms.rules'
2025-05-14 15:35:33.971 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'debug.rules'
2025-05-14 15:36:35.528 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'debug.rules'
2025-05-14 15:36:58.927 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'intrusion.rules'
2025-05-14 15:37:20.736 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'network.rules'
2025-05-14 15:37:34.351 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'openhab.rules'
2025-05-14 15:37:35.112 [INFO ] [ab.core.model.script.openhab_startup] - 🏁 OpenHAB ready.
2025-05-14 15:37:46.031 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'rflnk.rules'
2025-05-14 15:38:08.758 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'alarms.rules'
2025-05-14 15:40:10.643 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'alarms.rules'

Yes, this is correct behavior.

In OH 1-2 and since OH 4 this is the behavior for system started/start level reached triggers. For a brief time in OH 3 the rule would only trigger once.

There are several very long issues and PRs where this was all hashed out, and the current behavior is what was decided is the correct behavior. When the rule is loaded, if the runlevel has already passed that rule will trigger, no matter how long ago that runlevel was reached.

Any time you make a change somewhere that might require a refresh of a rule, all the rules that come from text files are reloaded. One reason a rule might need to be reloaded is changes to Items because you might have changed the Group membership or removed an Item used to trigger a rule.

When a reload occurs the rule is removed from OH and then it’s readded. When it’s readded it appears as a new rule, and it sees that the start level has passed and it triggers.

I think some limit this a bit by putting all the start level triggered rules into one file, but I’m not positive about whether that would work.

If you use UI defined rules this problem is much less pronounced because rules are refreshed on an individual basis instead of reloading whole files. This means the start level triggered rules won’t get reloaded when you change Items (for example) because Items are not used to trigger that rule and it doesn’t get reloaded just because it’s in the same file as a bunch of other rules that need to be reloaded. These pretty much only get reloaded if you actually change the rule itself.

I’m not sure if there are any other viable ways to prevent them from triggering when using text files for rules though. You might be able to test for the system uptime and if that was long enough ago you know the rule probably already ran and doesn’t need to run again. See openHAB uptime in an item but pay close attention to the post by @jimtng where he shows how to get the uptime in milliseconds with a simple access to a Java class instead of needing an extra binding or to access the REST API.

Thank you for the crystal clear explanation. I’ll think about how to make a limitation on restarting the initialization of rules, for example, as other users did, or I’ll come up with something of my own. Thanks again!