diff --git a/Plugin/unraid/api/scriptinfo.php b/Plugin/unraid/api/scriptinfo.php index ffeacbd..fddec11 100644 --- a/Plugin/unraid/api/scriptinfo.php +++ b/Plugin/unraid/api/scriptinfo.php @@ -113,9 +113,20 @@ foreach ($searchFiles as [$label, $file]) { if ($body) $sections[] = ['source' => $label, 'body' => $body]; } +// Any AI enhancement that reads this script's output, and whether it is switched on. Kept out of +// $sections because it is not documentation: the sections above describe what the script does, and +// this describes something else that watches it. Merging them would put a claim in the doc panel +// that the script's own header cannot support. +// +// require_once here rather than at the top: ai_repair.php pulls in the findings layer, and a +// scheduler info request on a host with AI off has no reason to load it. +require_once dirname(__DIR__) . '/include/ai_repair.php'; +$enhancement = function_exists('vv_ai_script_enhancements') ? vv_ai_script_enhancements($id) : []; + echo json_encode([ - 'ok' => true, - 'name' => $name, - 'header' => $header, - 'sections' => $sections, + 'ok' => true, + 'name' => $name, + 'header' => $header, + 'sections' => $sections, + 'enhancement' => $enhancement ?: null, ]); diff --git a/Plugin/unraid/include/ai_repair.php b/Plugin/unraid/include/ai_repair.php index 8dad561..f5ca647 100644 --- a/Plugin/unraid/include/ai_repair.php +++ b/Plugin/unraid/include/ai_repair.php @@ -974,6 +974,64 @@ function vv_ai_watchdog_findings(?array $payload = null): array { return $found; } +// ── What the AI adds to which script ────────────────────────────────────────────────────────── +// Declared here rather than in each script's header, and that is a deliberate departure from +// api/scriptinfo.php's usual rule that the header next to the code is authoritative. It is +// authoritative about what the script does. An enhancement is not the script's behaviour — it is +// something else reading the script's output afterwards, implemented in PHP, gated by a flag the +// script has never heard of. Writing it into the bash header would put a description of PHP inside +// a file that cannot enforce it, and the two would drift the first time either changed. +// +// A script may appear here without the enhancement being about its output. sonarr discovery is +// listed because the triage is its safety net: discovery takes the first accessible root folder +// (playback_aware_sonarr_discovery.sh:750) with no regard for whether the show is anime, kids or +// general, and the classification scan is what notices the next night. That relationship is real, +// it explains where misfiled series come from, and until now it was written down nowhere. +const VV_AI_SCRIPT_ENHANCEMENTS = [ + 'Arrs_Stack/sonarr_classification_scan.sh' => [ + 'name' => 'Classification triage', + 'flag' => 'AI_ASSIST_DISCOVERY', + 'what' => 'This scan reports reverse-anime leaks and deliberately does not act on them — ' + . 'its own header calls them genuine judgement calls, because no metadata field ' + . 'separates a misfile from a deliberate choice. The triage sorts that bucket into ' + . 'misfiled, donghua, anime-adjacent or uncertain.', + 'acts' => 'Files a finding for misfiled and uncertain only. A misfiled series carries a ' + . 'Move action, which relocates the files — pressed by a person, never automatic.', + ], + 'Arrs_Stack/playback_aware_sonarr_discovery.sh' => [ + 'name' => 'Classification triage (safety net)', + 'flag' => 'AI_ASSIST_DISCOVERY', + 'what' => 'Discovery adds every show it finds to the first accessible root folder, without ' + . 'classifying it. Anything it places wrongly is caught by the classification scan ' + . 'that night, and the triage is what turns that scan\'s count into named titles.', + 'acts' => 'Nothing here. The enhancement runs against the classification scan\'s output, ' + . 'not against this script.', + ], + 'Orchestrators/watchdog_orchestrator.sh' => [ + 'name' => 'Watchdog findings', + 'flag' => 'AI_ASSIST_WATCHDOG', + 'what' => 'Turns the counters the watchdogs keep — skip lists, strikes past their limit, ' + . 'repeated restarts, unattended reboots, sustained pressure — into named findings ' + . 'instead of numbers on a card.', + 'acts' => 'Files findings only. The kind is not conf-bound, so it cannot autofix by ' + . 'construction rather than by a setting.', + ], +]; + +function vv_ai_script_enhancements(string $id = ''): array { + if ($id === '') return VV_AI_SCRIPT_ENHANCEMENTS; + $id = ltrim(trim($id), '/'); + if (!str_ends_with($id, '.sh')) $id .= '.sh'; + $e = VV_AI_SCRIPT_ENHANCEMENTS[$id] ?? null; + if (!$e) return []; + // The flag state travels with it. An enhancement listed against a script without saying + // whether it is switched on is the same trap AI_ASSIST_WATCHDOG was for months: a name that + // looks like a feature and does nothing. + $e['enabled'] = strtolower(trim((string)(vv_conf_vars()[$e['flag']] ?? 'false'))) === 'true' + && vv_ai_repair_enabled(); + return $e; +} + // ── Classification triage ───────────────────────────────────────────────────────────────────── // The one place in the media stack where a model beats the rule it is helping. // diff --git a/Plugin/unraid/pages/scheduler.php b/Plugin/unraid/pages/scheduler.php index b1d6405..e49b8df 100644 --- a/Plugin/unraid/pages/scheduler.php +++ b/Plugin/unraid/pages/scheduler.php @@ -4450,6 +4450,29 @@ function vvShowScriptInfoMode(name, hdr, id) { + ''; } + // Above the documentation, because it changes how the sections below should be read: a + // script whose output is being triaged behaves the same but is no longer the last word on + // what happens to what it reports. Stated with its switch, so "there is an enhancement" and + // "it is running" are never the same claim — AI_ASSIST_WATCHDOG spent months looking like a + // feature while reading nothing. + if (info.ok && info.enhancement) { + const e = info.enhancement; + const col = e.enabled ? '#4caf50' : '#555'; + html += '