Intelligente Video-Stats-Tiers (YouTube)
5-stufiges Tier-System (full / standard / light / paused / off) für YouTube-Per-Video-Statistiken. Reduziert die youtube.videos.list-API-Calls deutlich bei gleichzeitig besserer Datenqualität für 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 prüfen
php artisan youtube:backfill-stats-tier --dry-run
# Subprocess-Range (Default 10K Profile pro Subprocess) anpassen falls nötig
php artisan youtube:backfill-stats-tier --subprocess-range=5000
Manueller Reevaluate (z.B. nach Config-Änderung):
# 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 für aktive PRO-Channels) |
light | 526.325 | 96,6 % | Free-Profile (free_no_sync) + PRO mit niedriger Aktivität |
paused | 3.907 | 0,7 % | PRO ohne Upload-Aktivität (≥ 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), weil inaktive Channels auf paused/off klassifiziert werden. Die zwischenzeitliche 2-Tage-Kadenz für standard wurde am 2026-06-08 wieder auf täglich gesetzt (Quota reichlich vorhanden) — die Hauptersparnis kommt ohnehin aus der Inaktiv-Klassifizierung, nicht aus der Kadenz-Streckung.
Wie läuft 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 | Täglich | 02:00 UTC | YOUTUBE_STATS_REVIEW_TIME + YOUTUBE_STATS_REVIEW_TZ | 60 min |
youtube:snapshot-coverage | Wöchentlich (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 schützen 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 tatsächlich ans OS zurück. - 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 lässt sich der Orchestrator deaktivieren (z.B. für Tests oder kleine Datenmengen).
Monitoring
Beide Scheduled Commands sind vollständig 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 tatsächlich fälligen 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 — inaktive Channels werden auf
paused/offgesetzt (kein Per-Video-Sync). Aktive Tiers laufen seit 2026-06-08 wieder täglich (full,standard) bzw. alle 3 Tage (light), da Quota reichlich vorhanden ist. - User-Override: User können 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-Änderung wird in
extended_stats_tier_changesgeloggt + scheduler/reactivation-Änderungen werden mit 14-Tage-Throttle dem Workspace-Owner zugestellt. Inhalt der Notification: TitelStats-Tier geändert: {handle}, BodyTier: {prev} → {new}. Grund: {reason}., Iconchart-bar, URL führt direkt zum ersten Watcher des Owners, der dieses Profil als Source führt. 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 zurück 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 | Täglich | 50 |
standard | Moderate Aktivität (3-9 Videos in 30 Tagen) | Täglich | 20 |
light | Niedrige Cadence (1-2 Videos in 30 Tagen) | Alle 3 Tage | 5 |
paused | Kein Upload in den letzten 30+ Tagen | Kein Per-Video-Sync | 0 |
off | Hart deaktiviert (Admin-Override) | Kein Sync überhaupt | 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). Änderungen in der ENV greifen sofort, daVideoStatsTier::snapshotIntervalDays()per Aufruf liest. Der Skip-Check inSyncYouTubeVideoStats::handle()(Job-Level) prüftlast_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-Grundsätze
| 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 ausschließlich User-Override (PRO). |
| G3 | Heuristik liefert NIEMALS off — off ist ausschließlich Admin-Override. |
| G4 | extended_stats_locked = true → Heuristik lässt den Tier unberührt. |
| G5 | Free-User-Cap: Free-User können bis free_max_lock_tier (Default standard) sperren. |
| G6 | Audit-Insert in extended_stats_tier_changes ist die einzige Source-of-Truth für 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` möglich
'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. Standardmäßig nur Profile mit extended_stats_review_due <= now().
| Flag | Wirkung |
|---|---|
--force | Ignoriert review_due — alle Profile reklassifizieren. |
--dry-run | Schreibt nichts; Ausgabe zeigt geplante Änderungen. |
--profile=ID | Nur einzelnes Profil klassifizieren. |
--limit=N | Maximal N Profile pro Lauf. |
Der Command:
- Lädt 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 — läuft einmalig nach Migration. Setzt für alle Profile ohne Tier den Erst-Tier.
php artisan youtube:snapshot-coverage
Wöchentlicher 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 für 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: tägliche 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();
// Wöchentlicher 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-Einträge 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 wöchentlichen Snapshots (4w / 13w / 26w / 52w / all).
User-UI
/watchers/{watcher} zeigt für YouTube-Quellen eine Tier-Card mit:
- Aktuellem Tier + Reason
- Lock-Status + nächster Prüfungstermin
- Tier-Override-Buttons (Free-Cap auf
standard) - Tier-Verlauf (kollabierbar, letzte 10 Einträge 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-Änderungen 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;
-- Geschätzte 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 | Nächste Klassifizierung. Wird im Reevaluate-Command als WHERE-Filter genutzt. |
extended_stats_locked | boolean | false | User/Admin hat Tier gesperrt — Heuristik hält Abstand. |
Indexe:
(extended_stats_tier)— Tier-Filter(extended_stats_review_due)— Reevaluate-Command-Filter
extended_stats_tier_changes (Audit-Tabelle)
Vollständige Tier-Verlaufs-History. Wird vom Listener HandleVideoStatsTierChange gefüllt.
| 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 | Änderungszeitpunkt |
youtube_coverage_weekly_snapshots
~30 Spalten Coverage-Metriken pro Snapshot-Datum. Datengrundlage für 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 möglich (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 für aktive Profile (Tage)
YOUTUBE_STATS_REVIEW_DEFAULT=7
# Reklassifizierungs-Intervall für '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=1
YOUTUBE_STATS_INTERVAL_LIGHT=3
# 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-Änderungen (default OFF)
YOUTUBE_STATS_NOTIFY_EMAIL=false
# Default-Tier für neue PRO-Profile (Backfill)
YOUTUBE_STATS_INITIAL_PRO=standard
# Free-User-Cap: bis zu welchem Tier dürfen Free-User selbst sperren? (full|standard|light|paused)
YOUTUBE_STATS_FREE_MAX_LOCK=standard
# Schedule-Zeit für den täglichen Reevaluate-Run (HH:MM, Default 02:00 UTC)
YOUTUBE_STATS_REVIEW_TIME=02:00
YOUTUBE_STATS_REVIEW_TZ=UTC
# Schedule für den wöchentlichen 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 übersprungen
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 läuft 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-Prüfung und Snapshot-Interval-Skip. Admin-/User-Force-Aktionen laufen immer. - Tier
offist exklusiv Admin: Reguläre User könnenoffnicht via Settings setzen — nur Admin via/admin/youtube/video-stats-tiers. - Admin-Override-Lock ist stärker 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 prüft PRE-Insert: Damit der eigene Audit-Insert den Throttle nicht selbst triggert. 14 Tage pro Profil, nur für
schedulerundreactivationTriggers. - Heartbeat skipt Dry-Run:
--dry-runbeiyoutube:reevaluate-stats-tierschreibt keinen Cron-Heartbeat — verhindert dass manuelle Tests einen tatsächlich fälligen Schedule-Status maskieren.