Intelligente Video-Stats-Tiers (YouTube)
5-stufiges Tier-System (full / standard / light / paused / off) fuer YouTube-Per-Video-Statistiken. Reduziert die youtube.videos.list-API-Calls deutlich bei gleichzeitig besserer Datenqualitaet fuer aktive Channels.
Quickstart — Wie anstoßen
Initialer Backfill (einmalig nach Migration):
# Alle 545K YT-Profile klassifizieren (Subprocess-Orchestrator-Mode aktiv)
php artisan youtube:backfill-stats-tier
# Mit Dry-Run vorab pruefen
php artisan youtube:backfill-stats-tier --dry-run
# Subprocess-Range (Default 10K Profile pro Subprocess) anpassen falls noetig
php artisan youtube:backfill-stats-tier --subprocess-range=5000
Manueller Reevaluate (z.B. nach Config-Aenderung):
# Alle Profile sofort reklassifizieren (ignoriert review_due)
php artisan youtube:reevaluate-stats-tier --force
# Einzelnes Profil debuggen
php artisan youtube:reevaluate-stats-tier --profile=42 --dry-run
# Subprocess deaktivieren (kleine Datenmenge → in-process schneller)
php artisan youtube:reevaluate-stats-tier --no-subprocess
Coverage-Snapshot manuell:
php artisan youtube:snapshot-coverage # persistiert
php artisan youtube:snapshot-coverage --dry-run # nur JSON-Output
Initialer Backfill (2026-04-28)
Der erste Backfill-Run auf Produktion klassifizierte 544.571 YouTube-Profile mit folgender Verteilung:
| Tier | Anzahl | Anteil | Bedeutung |
|---|---|---|---|
full | 0 | 0 % | Per G2 nie automatisch — nur User-Override |
standard | 14.339 | 2,6 % | PRO-Profile mit ≥ 3 Videos/30d (Default fuer aktive PRO-Channels) |
light | 526.325 | 96,6 % | Free-Profile (free_no_sync) + PRO mit niedriger Aktivitaet |
paused | 3.907 | 0,7 % | PRO ohne Upload-Aktivitaet (≥ 30d kein Upload) |
off | 0 | 0 % | Admin-Override-only (Hard-Failure-Marker) |
→ Per-Video-Snapshot-Pool reduziert sich auf ~14.000 Channels (vorher: ~21.000 PRO-Profile in 1:1-Sync). Sub-Seg standard mit 2-Tage-Kadenz statt taeglich → Quota-Einsparung in Per-Video-Pool ca. 50 % gegenueber dem alten 1:1-Modell.
Wie laeuft das regelmäßig
Beide Scheduler-Jobs sind in routes/console.php registriert und laufen automatisch mit dem normalen Forge-Cron (schedule:run jede Minute):
| Command | Frequenz | Default-Zeit | .env-Override | withoutOverlapping |
|---|---|---|---|---|
youtube:reevaluate-stats-tier | Taeglich | 02:00 UTC | YOUTUBE_STATS_REVIEW_TIME + YOUTUBE_STATS_REVIEW_TZ | 60 min |
youtube:snapshot-coverage | Woechentlich (Sonntag) | 04:00 UTC | YOUTUBE_STATS_COVERAGE_TIME + YOUTUBE_STATS_COVERAGE_DAY (0=Sonntag) + YOUTUBE_STATS_COVERAGE_TZ | 60 min |
Kein manuelles Cron-Setup notwendig. Schedule-Zeiten sind .env-konfigurierbar — der Admin-UI-Text passt sich dynamisch an.
Memory-Strategie (128 MB Limit)
Beide Iterativen Commands schuetzen sich gegen Memory-Probleme bei 545K+ Profilen:
- Subprocess-Orchestrator-Mode (Default): Top-Level-Aufruf splittet den Range in 10K-Profile-Chunks und ruft sich selbst per
Process::run(PHP_BINARY ...)auf. PHP gibt freigegebene Memory-Pages zwischen Subprocess-Iterationen tatsaechlich ans OS zurueck. - In-Process-Chunks: Innerhalb jedes Subprocess wird in 1K-Profile-Chunks per CTE-Query geladen, mit
gc_collect_cycles()zwischen Chunks. DB::disableQueryLog()zwingend in allen 3 Commands (CLAUDE.md).
Mit --no-subprocess Flag laesst sich der Orchestrator deaktivieren (z.B. fuer Tests oder kleine Datenmengen).
Monitoring
Beide Scheduled Commands sind vollstaendig im Monitoring-Stack:
- Heartbeat via
WritesCronHeartbeat-Trait — Heartbeat-Keys:youtube_stats_tier_review+youtube_coverage_snapshot. Bei--dry-runwird der Heartbeat NICHT geschrieben (sonst maskiert ein Test einen tatsaechlich faehlligen Schedule). /admin/log-queueHeartbeats-Tabelle zeigt Last-Run + Status (viaCronStatusService::SCHEDULE_MAP)./admin/update-statusDaily-Pipeline-Status zeigt Run-History + Retry-Button (viaDailyPipelineStatus::RETRY_COMMANDS).config/postbox.phpcron_heartbeats: max_minutes-Schwellen + sla_weight (Reevaluate=3, Snapshot=2) → Health-Score-Aggregation.
Ziele
- Quota-Reduktion: Aggressivere Defaults —
full(taegliche Per-Video-Snapshots) ist nur fuer hochaktive PRO-Channels. Default fuer alle anderen iststandard(alle 2 Tage) bispaused(kein Per-Video-Sync). - User-Override: User koennen ihren Tier selbst sperren — Free-User bis
standard, PRO-User bisfull. - Auto-Reaktivierung: Wenn ein pausierter Channel wieder uploadet, wird er automatisch reklassifiziert.
- Audit + Notification: Jede Tier-Aenderung wird in
extended_stats_tier_changesgeloggt + scheduler/reactivation-Aenderungen werden mit 14-Tage-Throttle dem Workspace-Owner zugestellt. Inhalt der Notification: TitelStats-Tier geaendert: {handle}, BodyTier: {prev} → {new}. Grund: {reason}., Iconchart-bar, URL fuehrt direkt zum ersten Watcher des Owners, der dieses Profil als Source fuehrt. Bei reinen User-/Admin-Triggers (user_override,admin_override,admin_disabled) feuert keine Notification, da der Auslöser selbst der User ist.
Admin-PRO-Disable (Plan 67)
Admin kann PRO + Extended-Stats für ein YouTube-Profil dauerhaft deaktivieren. Der youtube:auto-activate-pro-Cron ignoriert blockierte Profile — aber seit 2026-05-25 ist die Auto-Aktivierung sowieso global deaktiviert (YOUTUBE_PRO_AUTO_ACTIVATION_ENABLED=false). Der Lock-Flag pro_auto_activation_blocked_at bleibt trotzdem wichtig: er signalisiert anderen Code-Pfaden (Repair-Command, evtl. zukünftige Re-Aktivierungs-Logik) "Hände weg, das Profil wurde bewusst deaktiviert". Nur der User kann den Block wieder aufheben.
Admin-Aktionen (4 Stellen):
/admin/social-profiles→ Button "PRO deaktivieren"/admin/youtube-management/video-stats-tiers→ Button "PRO deaktivieren"- Watcher-Detail-Seite (Livewire-Action) → "Erweiterte Video-Statistiken deaktivieren" (Admin-only)
- Watcher-Detail-Form (POST-Route) →
DisableYouTubeVideoAutoSyncController(seit 2026-05-25 ebenfalls Plan-67-konform — vorher fehlten dort die Lock-Felder, Userreport 2026-05-25)
Was passiert bei Admin-Disable (alle 4 Pfade jetzt identisch):
pro_enabled→ falsepro_auto_activation_blocked_at→ jetzt (Block gesetzt)extended_stats_tier→ offextended_stats_reason→admin_disabledextended_stats_locked→ trueextended_stats_reviewed_at→ jetztextended_stats_review_due→ nullauto_sync_enabled→ false (auf youtube_video_syncs)VideoStatsTierChanged-Event wird dispatcht
User-Re-Enable: Wenn ein User auf der Watcher-Seite einen aktiven Tier wählt (z.B. Standard), wird der Block aufgehoben + PRO re-enabled + Tier auf standard gesetzt (Reevaluator darf den Tier später anpassen, weil extended_stats_locked zurueck auf false geht).
Admin-Filter: In /admin/social-profiles unter PRO-Filter → "Auto-Activate blockiert".
Cleanup historischer Video-Metriken (seit 2026-05-27)
Nach Plan-67-Disable bleiben die historischen youtube_video_daily_metrics-Rows der deaktivierten Profile in der DB liegen — bei ~13.000 disablten Profilen mit jeweils ~10-50 Videos × hunderten Tagen Metriken summiert sich das auf ~20 GB.
Cleanup via php artisan youtube:prune-inactive-video-metrics --dry-run (siehe Commands-Doku). Triple-Lock-Schutz garantiert dass aktive PRO-Profile NICHT angefasst werden: ein Profil bleibt nur verschont wenn platform=youtube UND tracking_enabled=true UND pro_enabled=true UND youtube_video_syncs.auto_sync_enabled=true.
Cleanup zugehöriger Videos + Thumbnails (seit 2026-05-28)
Folge-Cleanup zu prune-inactive-video-metrics: der erste Lauf löschte nur Daily-Metric-Rows, aber youtube_videos-Rows und ihre Thumbnails (R2/local Storage-Disk) blieben bestehen. Bei ~13k disablten Profilen × ~50 Videos × ~4 Thumbnail-Varianten = bis zu 2,6M nutzlose Storage-Files.
Cleanup via php artisan youtube:prune-inactive-video-records --dry-run (siehe Commands-Doku). Identische Triple-Lock-Logik. Per Batch: erst Thumbnail-Paths aus 4 Spalten sammeln und via Storage::disk('public')->delete([...]) löschen (best-effort), dann DB-DELETE — youtube_video_scores und Reste der youtube_video_daily_metrics werden via cascadeOnDelete() mitgelöscht.
Tier-Stufen
| Tier | Bedeutung | Per-Video-Snapshot | Top-Video-Limit |
|---|---|---|---|
full | Hochaktiver PRO-Channel mit ≥10 Videos in 30 Tagen | Taeglich | 50 |
standard | Moderate Aktivitaet (3-9 Videos in 30 Tagen) | Alle 2 Tage | 20 |
light | Niedrige Cadence (1-2 Videos in 30 Tagen) | Alle 7 Tage | 5 |
paused | Kein Upload in den letzten 30+ Tagen | Kein Per-Video-Sync | 0 |
off | Hart deaktiviert (Admin-Override) | Kein Sync ueberhaupt | 0 |
Source of Truth: Werte stammen aus
config/postbox.php→youtube_stats_tier.*. Defaults sind oben dokumentiert; alle sind via ENV-Variablen override-bar (YOUTUBE_STATS_INTERVAL_FULL/STANDARD/LIGHT,YOUTUBE_STATS_TOP_FULL/STANDARD/LIGHT). Aenderungen in der ENV greifen sofort, daVideoStatsTier::snapshotIntervalDays()per Aufruf liest. Der Skip-Check inSyncYouTubeVideoStats::handle()(Job-Level) prueftlast_sync_at > now()->subDays($intervalDays)— Cycle-Length entspricht dem Intervall (interval=2 → Sync alle 2 Tage, interval=3 → alle 3 Tage, etc.).
paused und off blockieren Snapshot-Inserts in youtube_video_daily_metrics ueber VideoStatsTier::allowsVideoSnapshots().
Heuristik-Grundsaetze
| Code | Garantie |
|---|---|
| G1 | Profil-Daily-Metriken (social_profile_daily_metrics) werden NIE getroffen — Plan 56 betrifft nur Per-Video-Stats. |
| G2 | Heuristik liefert NIEMALS full automatisch — full ist ausschliesslich User-Override (PRO). |
| G3 | Heuristik liefert NIEMALS off — off ist ausschliesslich Admin-Override. |
| G4 | extended_stats_locked = true → Heuristik laesst den Tier unberuehrt. |
| G5 | Free-User-Cap: Free-User koennen bis free_max_lock_tier (Default standard) sperren. |
| G6 | Audit-Insert in extended_stats_tier_changes ist die einzige Source-of-Truth fuer Tier-Verlauf. |
| G7 | Notifications gehen nur bei triggered_by ∈ {scheduler, reactivation} raus, mit 14-Tage-Throttle. |
| G8 | IntelligentVideoStatsTier::classify() ist eine reine Funktion (keine DB-Side-Effects). |
Konfiguration
config/postbox.php → youtube_stats_tier:
'pause_days_no_upload' => env('YOUTUBE_STATS_PAUSE_DAYS', 30), // ab welchem Upload-Stand ist `paused`
'high_activity_per_month' => env('YOUTUBE_STATS_HIGH_ACTIVITY', 10), // ab wieviel Videos/30d ist `full` moeglich
'moderate_activity_min' => env('YOUTUBE_STATS_MODERATE_MIN', 3), // ab wieviel Videos/30d ist `standard`
'dormant_days' => env('YOUTUBE_STATS_DORMANT_DAYS', 180), // ab wann ist Channel dormant
'review_default_days' => env('YOUTUBE_STATS_REVIEW_DEFAULT', 7), // Reklassifizierungs-Intervall (active)
'review_paused_days' => env('YOUTUBE_STATS_REVIEW_PAUSED', 14), // Reklassifizierungs-Intervall (paused)
'reevaluate_chunk_size' => env('YOUTUBE_STATS_REEVAL_CHUNK', 1000), // Chunk-Size im Reevaluate-Command
'free_max_lock_tier' => env('YOUTUBE_STATS_FREE_MAX_LOCK', 'standard'), // Free-User-Cap (kein Full)
Commands
php artisan youtube:reevaluate-stats-tier
Klassifiziert alle Profile mit tracking_enabled neu. Standardmaessig nur Profile mit extended_stats_review_due <= now().
| Flag | Wirkung |
|---|---|
--force | Ignoriert review_due — alle Profile reklassifizieren. |
--dry-run | Schreibt nichts; Ausgabe zeigt geplante Aenderungen. |
--profile=ID | Nur einzelnes Profil klassifizieren. |
--limit=N | Maximal N Profile pro Lauf. |
Der Command:
- Laedt Activity-Signals via CTE-Query (
ActivitySignalsLoader). - Klassifiziert via
IntelligentVideoStatsTier::classify()(G8 — pure). - Persistiert nur, wenn
decision->changed === true. - Dispatcht
VideoStatsTierChanged→ Listener schreibt Audit + Notification. - Setzt
extended_stats_review_dueaufreview_default_daysbzw.review_paused_days.
php artisan youtube:backfill-stats-tier
Initialer Backfill — laeuft einmalig nach Migration. Setzt fuer alle Profile ohne Tier den Erst-Tier.
php artisan youtube:snapshot-coverage
Wochentlicher Snapshot der Coverage-Metriken in youtube_coverage_weekly_snapshots. Aggregiert:
- Profile-Counts (total, pro, free, blocked, sanitized)
- Tier-Verteilung (full / standard / light / paused / off) — nur Profile mit
pro_enabled=true(siehe Hinweis unten) - Per-Video-Snapshot-Counts der letzten 30 Tage
- Frische-Verteilung (≤7d / 7-30d / >30d)
- YouTube-Quota-Verbrauch als 7-Tage-Mittel aus
google_api_quota_usages(gemessen, mit Heuristik-Fallback) - Storage-Size der
youtube_video_daily_metrics-Tabelle
Datengrundlage fuer das Coverage-Trends-Dashboard.
Quota-Spalte estimated_daily_quota_units (seit 2026-05-27):
- Liest primär
AVG(used_value)ausgoogle_api_quota_usagesfürservice='youtube.googleapis.com'der letzten 7 Tage (CURRENT_DATE-Range). Das ist der GEMESSENE Quota-Verbrauch. - Fallback (wenn keine Messdaten verfügbar): Heuristik
tier_full × 50 + tier_standard × 50 × 0.43 + tier_light × 50 × 0.14. Die Heuristik nutzt diepro_enabled=true-gefilterten Tier-Counts (vorher zählte sie alle ~469k Tracking-Profile, davon ~467k Default-tier=light × 14% Sync-Frequenz = 3,3M Units-Beitrag — komplett unrealistisch, da nur ~1.200 PRO-Profile tatsächlich syncen). - Dashboard-Label im Coverage-Trends-Widget:
Quota /Tag (7d-Avg).
Schedule
routes/console.php:
// Plan 56 v2: taegliche Reklassifizierung (Default 02:00 UTC, .env-konfigurierbar)
Schedule::command('youtube:reevaluate-stats-tier')
->dailyAt(config('postbox.youtube_stats_tier.review_schedule_time'))
->timezone(config('postbox.youtube_stats_tier.review_schedule_timezone'))
->withoutOverlapping();
// Wochentlicher Coverage-Snapshot (Default Sonntag 04:00 UTC, .env-konfigurierbar)
Schedule::command('youtube:snapshot-coverage')
->weeklyOn(config('postbox.youtube_stats_tier.coverage_schedule_day'), config('postbox.youtube_stats_tier.coverage_schedule_time'))
->timezone(config('postbox.youtube_stats_tier.coverage_schedule_timezone'))
->withoutOverlapping();
Beide Commands schreiben WritesCronHeartbeat-Eintraege und sind in config/postbox.php → cron_heartbeats registriert.
Auswertung
Admin-UIs
/admin/youtube/video-stats-tiers— Tabelle aller Profile mit Tier-Filter, Set-Tier-Action, Lock-Toggle, Force-Review-Button./admin/youtube/video-coverage-trends— ApexCharts-Trends auf Basis der wochentlichen Snapshots (4w / 13w / 26w / 52w / all).
User-UI
/watchers/{watcher} zeigt fuer YouTube-Quellen eine Tier-Card mit:
- Aktuellem Tier + Reason
- Lock-Status + naechster Pruefungstermin
- Tier-Override-Buttons (Free-Cap auf
standard) - Tier-Verlauf (kollabierbar, letzte 10 Eintraege aus
extended_stats_tier_changes)
Direkte SQL-Auswertung
-- Tier-Verteilung aktuell
SELECT extended_stats_tier, COUNT(*) AS profiles
FROM social_profiles
WHERE platform = 'youtube' AND tracking_enabled = TRUE
GROUP BY extended_stats_tier
ORDER BY profiles DESC;
-- Tier-Aenderungen der letzten 7 Tage
SELECT triggered_by, reason, COUNT(*) AS changes
FROM extended_stats_tier_changes
WHERE changed_at >= NOW() - INTERVAL '7 days'
GROUP BY triggered_by, reason
ORDER BY changes DESC;
-- Geschaetzte Quota-Reduktion (Snapshot-Counts)
SELECT
snapshot_date,
estimated_daily_quota_units,
snapshots_total_30d,
avg_snapshots_per_video
FROM youtube_coverage_weekly_snapshots
ORDER BY snapshot_date DESC
LIMIT 13;
Datenbank
social_profiles (neue Spalten)
| Spalte | Typ | Default | Bedeutung |
|---|---|---|---|
extended_stats_tier | varchar (Enum-Cast) | null | Aktiver Tier. |
extended_stats_reason | varchar | null | Reason aus letzter Klassifizierung (moderate_activity, no_recent_upload, user_override, …). |
extended_stats_reviewed_at | timestamp | null | Letzte Klassifizierung. |
extended_stats_review_due | timestamp | null | Naechste Klassifizierung. Wird im Reevaluate-Command als WHERE-Filter genutzt. |
extended_stats_locked | boolean | false | User/Admin hat Tier gesperrt — Heuristik haelt Abstand. |
Indexe:
(extended_stats_tier)— Tier-Filter(extended_stats_review_due)— Reevaluate-Command-Filter
extended_stats_tier_changes (Audit-Tabelle)
Vollstaendige Tier-Verlaufs-History. Wird vom Listener HandleVideoStatsTierChange gefuellt.
| Spalte | Typ | Bedeutung |
|---|---|---|
social_profile_id | bigint | FK |
previous_tier | varchar | Letzter Tier (kann null sein bei Erst-Klassifizierung) |
new_tier | varchar | Neuer Tier |
reason | varchar | Reason-Code |
triggered_by | varchar | scheduler / reactivation / user / admin / backfill |
changed_at | timestamp | Aenderungszeitpunkt |
youtube_coverage_weekly_snapshots
~30 Spalten Coverage-Metriken pro Snapshot-Datum. Datengrundlage fuer das Coverage-Trends-Dashboard.
.env-Block
# Plan 56 v2 — Intelligente Video-Stats-Tiers
# Pause-Schwelle: kein Upload in N Tagen → Tier 'paused'
YOUTUBE_STATS_PAUSE_DAYS=30
# Ab wieviel Videos/30d ist 'full' theoretisch moeglich (User-Lock-Voraussetzung)
YOUTUBE_STATS_HIGH_ACTIVITY=10
# Ab wieviel Videos/30d ist 'standard'
YOUTUBE_STATS_MODERATE_MIN=3
# Ab wann gilt Channel als dormant
YOUTUBE_STATS_DORMANT_DAYS=180
# Reklassifizierungs-Intervall fuer aktive Profile (Tage)
YOUTUBE_STATS_REVIEW_DEFAULT=7
# Reklassifizierungs-Intervall fuer 'paused' Profile (Tage)
YOUTUBE_STATS_REVIEW_PAUSED=14
# Chunk-Size im Reevaluate-Command
YOUTUBE_STATS_REEVAL_CHUNK=1000
# Snapshot-Intervalle pro Tier (Tage zwischen Per-Video-Snapshots)
YOUTUBE_STATS_INTERVAL_FULL=1
YOUTUBE_STATS_INTERVAL_STANDARD=2
YOUTUBE_STATS_INTERVAL_LIGHT=7
# Top-Limit pro Tier (Anzahl Videos pro Run)
YOUTUBE_STATS_TOP_FULL=50
YOUTUBE_STATS_TOP_STANDARD=20
YOUTUBE_STATS_TOP_LIGHT=5
# Optional: Admin-Mail bei Tier-Aenderungen (default OFF)
YOUTUBE_STATS_NOTIFY_EMAIL=false
# Default-Tier fuer neue PRO-Profile (Backfill)
YOUTUBE_STATS_INITIAL_PRO=standard
# Free-User-Cap: bis zu welchem Tier duerfen Free-User selbst sperren? (full|standard|light|paused)
YOUTUBE_STATS_FREE_MAX_LOCK=standard
# Schedule-Zeit fuer den taeglichen Reevaluate-Run (HH:MM, Default 02:00 UTC)
YOUTUBE_STATS_REVIEW_TIME=02:00
YOUTUBE_STATS_REVIEW_TZ=UTC
# Schedule fuer den woechentlichen Coverage-Snapshot (Default Sonntag 04:00 UTC)
# Day: 0=Sonntag, 1=Montag, ..., 6=Samstag
YOUTUBE_STATS_COVERAGE_TIME=04:00
YOUTUBE_STATS_COVERAGE_DAY=0
YOUTUBE_STATS_COVERAGE_TZ=UTC
Tests
tests/Unit/Services/YouTube/IntelligentVideoStatsTierTest.php(13 Tests)- G1-G8 Garantien
- User-Lock, Free-Cap
- Klassifizierungs-Pfade (full / standard / light / paused / off)
tests/Feature/Commands/Youtube/ReevaluateVideoStatsTierTest.php(6 Tests)- Persistierung + Audit-Insert + Event-Dispatch
- Dry-Run schreibt nichts
- Locked-Profile werden uebersprungen
review_duein der Zukunft → Skip- Snapshot-Coverage-Command
tests/Feature/Listeners/HandleVideoStatsTierChangeTest.php(6 Tests)- Audit-Insert bei jedem Trigger-Typ
- Notification nur bei
scheduler/reactivation - 14-Tage-Throttle pro Profil
- Throttle laeuft nach 15+ Tagen ab
Garantien & Edge-Cases
- Manuelle Trigger umgehen den Tier-Filter:
SyncYouTubeVideoStats-Job mittriggeredManually=true(Watcher "Video-Statistiken abrufen", CLI--profile-id) bypasst Tier-Pruefung und Snapshot-Interval-Skip. Admin-/User-Force-Aktionen laufen immer. - Tier
offist exklusiv Admin: Regulaere User koennenoffnicht via Settings setzen — nur Admin via/admin/youtube/video-stats-tiers. - Admin-Override-Lock ist staerker als User-Lock: Wenn
extended_stats_reason === 'admin_override', kann der Lock nur von Admins aufgehoben werden — verhindert dass Watcher-Owner Hard-Failure-Marker wegklicken. - Notification-Throttle prueft PRE-Insert: Damit der eigene Audit-Insert den Throttle nicht selbst triggert. 14 Tage pro Profil, nur fuer
schedulerundreactivationTriggers. - Heartbeat skipt Dry-Run:
--dry-runbeiyoutube:reevaluate-stats-tierschreibt keinen Cron-Heartbeat — verhindert dass manuelle Tests einen tatsaechlich faehlligen Schedule-Status maskieren.