Three ways a misconfiguration could leave no trace an operator would see, plus a hide-rule setting that was accepted and then ignored. All four came out of one support case where the plugin was behaving correctly every time and the user could not tell why.
A run time the scheduler cannot use is now named instead of discarded
_parse_scheduled_times threw away any entry it could not use without recording it, and Validate Configuration ran a weaker check of its own that only asked whether an entry was four digits. That check accepted 2400, which the scheduler rejects, so the settings form called a schedule good and the midnight run never happened.
Validate Configuration now judges run times with the same parser the scheduler arms itself from, so the two can no longer disagree, and both it and Save Schedule name every entry they will ignore. Midnight is 0000; the valid range is 0000 to 2359.
Channel groups that matched nothing are reported on every run
The scan already worked these out, but wrote them only to the container log and put them in the result message only when the scan found no channels at all. A partial miss was invisible: one reported case had three of six group names matching nothing and still read as a clean run.
They are now named in the result message and in the CSV header on every run. A group entry containing | also gets an explicit note that Channel Groups is comma-separated, since carrying that character over from the regex fields glues several real group names into one that matches nothing.
Regex fields that matched nothing are reported
Nothing counted what the three regex settings actually matched, so a pattern that could never fire produced a run identical to one where it was working. The scan now counts them, warns on any that matched zero, lists the counts in the CSV header under Regex Field Matches, and states in the result message that these fields read the channel or stream name and never guide programme titles.
The inactive-regex count re-tests the names rather than counting hide decisions, because hide rules are first-match-wins and a decision count would report "matched nothing" for a working pattern that sits below another rule.
The two name-length hide rules take their cutoff from the tag
[ShortDescription] measured the text after a separator against a 15 written into the rule body, and [ShortChannelName] measured a separator-less name against a 25.
The part that made this a defect rather than a missing feature: the hide-rule parser has always split any [Name:N] tag and handed the number to the rule, and these two ignored it. So [ShortDescription:20] parsed without complaint, ran, and applied 15.
Both rules now read that number and fall back to their historical values, so a bare tag hides exactly what it always hid. On one provider's channels the fixed cutoff hid NCAAF 25: FS1 [1080p] (11 characters after the colon) and left NCAAF 26: SEC NETWORK [1080p] visible (19); [ShortDescription:25] now catches both.
One output change to expect
The reason text now records the cutoff that produced the decision, which changes the CSV hide_rule column for these two rules: they report as ShortDescription:15 and ShortChannelName:25 rather than as bare names, matching how [PastDate:0] and [UndatedAge:2] already report. If you group or filter CSV rows by that column, expect the change.
Documentation
README.md gains a Client Setup section for Jellyfin, Plex and Emby covering the profile-scoped playlist and guide URLs, a worked example for [FutureDate:days] (a channel named for tomorrow's event is visible on purpose under the default [FutureDate:2]; use [FutureDate:0] for day-of only), a note that dummy guide titles come from the channel name even when Name Source is set to Stream Name, and nine new troubleshooting entries.
Compatibility
No setting or action was added, removed or renamed, and plugin.json fields are unchanged. Existing hide-rule lists behave exactly as before.