The header audit and per-folder docs pass produced a corpus with properties
worth capturing before they are forgotten: deterministic chunk boundaries,
section type as a retrieval filter, and an index that cannot leak credentials
because the conf files were never tracked.
The repo front page read like generic documentation rather than a record of
why any of this exists. Adds the origin story, the current honest state, real
failure history, and fixes drift in platform_*() naming, the fallback tier
variable pattern, and the Plugin/unraid script locations.
It is the front page and it read like a spec sheet. Adds the incidents the safeguards came
from, an honest note about what is actually switched on, and the layers built since.
The folder that translates the OS into Varaverk's vocabulary had no docs of its own, so the
adapter contract and the three-layer web UI were only discoverable by reading the code.
The fallback docs used an older COVERS naming in the wrong conf file, and rsync's merge-run
mode — the only path that carries --delete — was undocumented entirely.
The docs had drifted from the scripts — a script that no longer exists, three wrong variable
names, a reversed run order, and seven scheduled scripts that were never documented at all.
Finishes the pass: every script now documents its safeguards, and the deliberate absences
in the sourced libraries are recorded so they are not "corrected" later.
On this host "authelia" matches both Authelia and Authelia-Secondary, and taking the first
match writes the wrong instance into the conf every other script then trusts.
A copy truncates the live conf and writes into it, so anything sourcing load_config.sh
during that window reads a partial file with empty path variables.
Artist and title strings are full of regex metacharacters, so a new candidate could match
an unrelated history entry and be silently skipped as already seen.
The estimates in the original note were wrong in both directions, and Unraid's Apply does
not reliably recreate a container, which is worth knowing before the next tuning pass.
Captures the reasoning behind a not-yet-built feature so the constraints survive the
session, chiefly that AI stays enhancement-only and never load-bearing.
MY_ID is not exported and is unset until detect_hosts runs, so the DDNS and NPM checks
were silently resolving empty config and skipping themselves every cycle.
No such rc.d service exists on Unraid (emhttpd isn't managed via a
standalone rc.emhttp script) and nothing in the codebase calls
platform_restart_service/platform_stop_service with 'emhttp' — confirmed
during a post-7.3.2-upgrade audit of all platform-specific paths.
Unraid's real event name is unmounting_disks, not disks_unmounting — the
mismatch meant emhttp_event never found this hook, so array_stopping.sh
never ran on real array stop.
Extends the Sonarr-only corruption scan into a generic per-arr loop (same
pattern as arr_full_rescan.sh) instead of a second script, since the
scan/strike/remediate logic is identical and only the API shape differs.
Radarr's moviefile list is fetched batched to include secondary/alternate-
edition files, not just each movie's primary file.
Also fixes two bugs found while testing: build_arr_path_map()'s internal
non-local `for key in ...` loop was clobbering the per-arr API key
variable, and arr_api()'s error output (stdout, not stderr) was getting
appended into the batch fetch file on any single failed call, corrupting
jq's parse of the whole batch and silently zeroing out that arr's results.
stop_local_containers() reads LOCAL_CRITICAL_CONTAINER_NAMES, which was
never set here — only rsync.sh sets it, in a child process that can't
propagate back. Local stop always no-op'd, so the rebuild-on-new-image
step never had anything to act on.
Neither remove_from_skip_list() nor clear_intentional_stop() can ever
fire for a container that's been fully uninstalled — both only trigger
on seeing it running again. Confirmed live 2026-07-19: Healarr sat on
the skip list nagging "manual intervention needed" every single cycle
for weeks after being removed. Now checks docker inspect for each
entry every cycle and drops it automatically if the container no
longer exists at all.
Measured live: the apikey --name lookup takes 2.2-2.7s baseline with
the system idle, against a 5s timeout that left almost no margin.
Load spikes (confirmed correlated with resource_watchdog pressure
events in the same log window) pushed it over 5s, and the script
couldn't distinguish "timed out" from "genuinely missing" — it fell
through to the create path and logged a false renewal every time,
even though the on-disk key file's timestamp never actually changed.
Bumped to 15s for real headroom.
FAILURE_COUNTS persisted across runs but was never reset on success —
confirmed live 2026-07-19 that Sekirei S06E04 sat permanently flagged
chronic at count 4 despite already having hasFile=true. "Consecutive
failures" was really being measured as cumulative-for-all-time. Now
prunes any media_id's count at the end of each arr's pass if it no
longer appears in that run's problem-item set, so a resolved item's
history doesn't linger and falsely trip the circuit breaker later.
docker_update.sh rebuilds (stop+recreate) any container whose image
changed, in every mode — but for daily/weekly that was always followed
by the restart script's own unconditional pass, stopping and starting
the same container twice back to back. docker_update.sh now records
which containers it rebuilt this run to a file; docker_daily_restart.sh
and docker_weekly_restart.sh read it and skip those specifically,
still restarting everything else as before. A file older than
DOCKER_UPDATE_REBUILT_STALE_HOURS (default 12) is discarded rather
than trusted, so a missed or failed update run can't suppress a
restart indefinitely.
The candidate objects from /manualimport only nest ids under .series.id
and .movie.id, but Sonarr/Radarr's ManualImport command requires them
flattened to top-level seriesId/episodeIds or movieId — every call was
failing instantly with "Series/Movie with ID 0 does not exist" while the
caller only checked for HTTP 201 (accepted), so every "smart-imported"
this script has ever logged was actually a silent no-op. Now flattens
the ids before submitting and briefly polls the command afterward so a
fast failure falls through to the normal blocklist+research path instead
of being reported as success.
Template was missing theme.mkv/theme.mp4 and all trailer patterns,
including trailer-*.* for Trailarr's hyphenated TV trailer naming
(trailer-2.mkv) — a fresh install would hit the same false-positive
orphan bug just fixed live on prod.
Radarr 6+ supports multiple tracked files per movie (alternate editions/extras),
but the movie list's embedded movieFile.path only ever reflects the primary one.
Relying on it alone flagged legitimately-tracked secondary edition files as
orphans. Now batch-fetches /moviefile?movieId=X across all tracked movies to
catch every file, not just the primary.
Full-library scans are slow to reach any one show by chance — this lets a test
target a specific known-corrupt file/show without waiting on scan order.
Healarr does the same job but crashes on a Go concurrency bug (unsynchronized
map access) whenever multiple corruption events land close together — confirmed
via its own crash log, not fixable from our side. Processing one file at a time
here sidesteps the whole bug class instead of trying to work around it.
Delete + explicit EpisodeSearch rather than relying on Sonarr's own background
missing-search cycle, since that skips unmonitored episodes and this shouldn't.