Commit Graph
186 Commits
Author SHA1 Message Date
Gmer4Lfe 7f22bb0612 Run the PHP layer on the host's clock, not UTC
PHP defaults to UTC on Unraid while every bash script stamps local time, and the
two write into the same files — conf_changes.log was four hours out from every
log you would correlate it against. The parsing half was worse: vv_ai_syslog_ts()
reads local-time syslog lines through strtotime() under UTC, landing every event
four hours early, and the repair sweep bounds its scan to "since the last pass"
— so a fault that had just happened could read as four hours old and fall
outside the window. Also fixes date-string comparisons against bandwidth and
cleanup dbs, which bash writes with local dates.
2026-08-14 19:49:48 -04:00
Gmer4Lfe 7ebe8c0891 Judge load on the Watchdog page against the same count the watchdog uses
The page multiplies its core count by the RW_LOAD_* multipliers to colour the
load bar, and resource_watchdog.sh computes its real thresholds from nproc. The
page preferred the API's physical core count — 16 against 32 threads — so it
turned amber at load 32 and red at 48 while the watchdog did not reach soft
pressure until 64 or medium until 96. A page whose whole job is reporting what
the watchdogs think was reporting a crisis about a machine they considered idle.
The remote path already sent the partner's own nproc, so both nodes now agree.
2026-08-14 19:42:14 -04:00
Gmer4Lfe dbc0c8c6d2 Report container CPU as a share of the machine, matching Unraid's Docker page
docker stats sums CPU across every logical processor, so Jellyfin's "227%" is
2.3 threads busy — 7% of a 32-thread host, which is what Unraid shows and what
the operator was reading. The card was printing the raw figure and colouring it
against thresholds meant for a percentage, so ordinary background work looked
like an emergency; I twice flagged Jellyfin as pegged on that basis and was
wrong both times. Divides by nproc, not the watchdog's `cores` — that field is
the physical 16 because it compares load averages, and borrowing it here would
have looked reasonable and doubled every reading.
2026-08-14 19:39:14 -04:00
Gmer4Lfe 04a0dcb39a Let the media server card show the whole mesh, not just this host
A partner's Emby URL is http://localhost:8096 — true there, meaningless here —
so nobody queries a partner's media server directly. Each host reports its own
over the SSH call remote_arr_cache_writer.sh already makes, which keeps its API
keys on it and costs no extra connection. Local is the default: opening the tab
to check your own server should not first make you read past a partner's.
A partner with no cache is stated as not collected rather than omitted, because
an empty mesh view and a partner that is down look identical otherwise.
2026-08-14 19:30:23 -04:00
Gmer4Lfe 24710af156 Add media server cards, and stop re-counting whole libraries every minute
The counts need the entire library downloaded to compute six numbers — 16.0 MB
from Radarr and 5.5 MB from Sonarr, measured — and they were on the same
one-minute clock as queue depth and health, which pulled ~30 GB a day out of the
arrs to re-count records that had not changed. They now cache for fifteen
minutes against how often the numbers actually move: 7.6s to 3.0s per refresh.

Emby and Jellyfin get a card each: version, CPU, memory, uptime, streams, users
and transcodes, with update/restart flags. Two halves of one question — a server
answering happily at 145% CPU is a different situation from one at 8%, and
neither the app nor the container says so alone.
2026-08-14 19:23:10 -04:00
Gmer4Lfe 1381a526ab Surface the three media jobs that appeared in no tab at all
play_state_sync, media_shares_permissions and media_cleaner work on the same
files the arrs manage and were visible only by opening the Scheduler and reading
an orchestrator's log. Readable at all because run_orch_child() now writes a run
record and a per-script log for its children — this could not have been written
yesterday. Each card shows the sentence the script itself ended on rather than a
count re-derived here, since the three word their outcome differently and the
wording is the part worth reading.
2026-08-14 18:51:54 -04:00
Gmer4Lfe 04aba81ab3 Show orphan and junk sizes, and drop a cleanup field nothing could fill
The arr cleanups are entries in DAILY_MAINTENANCE_SCRIPTS, which the orchestrator
invokes with plain bash, so they never get the run record the primary parse needs
— every payload has come from the daily aggregate db. That path read every column
except the byte counts, leaving orphans_sz at its "0B" default, which is
invisible at zero orphans and would have read "12 orphans (0B)" the first time
there were any. 'total' had no source there and no reader anywhere, so it is gone.
Arr Sync now reads "disabled" rather than "never run" when its switch is off.
2026-08-14 18:15:56 -04:00
Gmer4Lfe 2cd1384786 Add timed mutes so a temporary problem stops needing a permanent exemption
Every existing exemption lasts until someone remembers to undo it, and nobody
does — Healarr has sat in a pressure list since it was uninstalled and seven
ignore entries name containers that are gone. A mute states when it ends and
then ends, capped by WATCHDOG_MUTE_MAX_HOURS so temporary is enforced rather
than intended. Applied where IGNORE_MAP is built, so all five check sites
inherit it, and shown with its countdown because an invisible suppression is
the thing being fixed.
2026-08-14 16:28:59 -04:00
Gmer4Lfe 9f32644c32 Show when a watchdog cycle last completed
Without it the page renders zeros, all-clear pills and green dots when the
orchestrator has stopped entirely — its healthiest appearance describing a
machine with no watchdogs, against a header promising absence is never drawn as
an all-clear. The heartbeat already existed; overdue is measured against the
cron schedule rather than an assumed fifteen minutes.
2026-08-14 16:18:14 -04:00
Gmer4Lfe a7387c27fb Mark watchdog list entries that name a container which no longer exists
Half the hard-pressure stop list is containers that are not installed, so at
level 3 it sheds half the memory the card implies. Badged on the lists where a
dead entry costs something and deliberately not on Scan ignore, where ignoring
something already gone is the point — a card of dashed pills by design would
teach the eye to skip the badge where it has to be believed.
2026-08-14 16:06:02 -04:00
Gmer4Lfe 2f8b7276a4 Give the watchdog counters a voice in the repair sweep
A fourth candidate source beside the arrs, the system log and container logs:
skip-listed containers, strikes past their limit, repeated restarts, unattended
reboots and sustained pressure become findings. The kind is deliberately not
conf-bound, so it cannot autofix by construction rather than by a switch — and
WATCHDOG_SCAN_IGNORE suppresses it, so a knowingly broken container stays quiet.
First consumer AI_ASSIST_WATCHDOG has ever had.
2026-08-14 12:01:18 -04:00
Gmer4Lfe cbcfbfaaad Stop bash arrays ending at the first ) inside a comment
Both the reader and the writer ran to the first closing paren, which is only the
array's own close if nothing inside contains one. HOST1_WATCHDOG_SCAN_IGNORE has
carried "(exit 127 — bad image)" for weeks: writes spliced into the middle of it
and were refused by bash -n with a bare false, and reads returned the entries
above it — the Rsync tab showed no intermediate scripts at all and 10 of 17
daily. Arrays now close on a ) that starts its own line, as confform already did.
2026-08-14 11:35:18 -04:00
Gmer4Lfe 1f4b751fbb Cache the watchdog payload, and stop reading trailing comments as conf values
The tab collected over SSH on every 30s poll — 8.3s a load with HOST2 down.
vv_parse_conf_scalar() captured to end of line, so a commented toggle parsed as
"true   # HOST2 back online": every threshold read right because (int) stops at
the first non-digit, and 38 booleans read wrong. The Fallback tab has been
showing failover disabled while it was on.
2026-08-14 10:45:17 -04:00
Gmer4Lfe c6b56d580e Size the Scheduler panel from the height the dock reports, not one read mid-animation
.vv-ai-chat transitions its height, so measuring the dock straight after setting
it returns the height it is animating away from — the view was cut short for a
dock that had already shrunk, leaving the difference as dead panel underneath.
2026-08-14 09:55:41 -04:00
Gmer4Lfe b358dfcf58 Report disk capacity in decimal GB so it matches Unraid and the drive label
The API mixes units per field — memory in bytes, disk size in KiB, fsSize and
fsUsed in kB — and one helper was guessing which from the magnitude of the
number, which also read any memory total under 100 GB as KB.
2026-08-13 18:07:09 -04:00
Gmer4Lfe 57e2b0d670 Let troubleshoot see the machine and answer with a route
It could describe a conf edit but not the switch that makes it, and knew
nothing about containers, pools or the array it was reasoning about.
2026-08-13 17:22:58 -04:00
Gmer4Lfe b7df54e4f3 Resume the dashboard chat from its own profile
It reopened whatever was touched last anywhere, so the card could land in a
Scheduler troubleshooting thread with no picker to get back.
2026-08-13 17:03:45 -04:00
Gmer4Lfe 2ccc93bb14 Send a bug report to one place, on purpose, after reading it
The page built its own markdown and nothing could send it; local and upstream
are different people, so neither falls back to the other.
2026-08-13 16:32:36 -04:00
Gmer4Lfe 2c1fd4c8bf Keep the assistant's reading apart from the log it read
A guess printed beside a quote is remembered as a second quote, and the next
reader inherits it as a finding.
2026-08-13 16:08:23 -04:00
Gmer4Lfe 466081aa66 Redact a bug before it is stored, and give it a report shape
Evidence was kept raw, so a key in a log line reached the page and the store;
and there was no format to send even once it was clean.
2026-08-13 15:58:29 -04:00
Gmer4Lfe be6bb3ffc5 Let a kept memory be taken back
Accepting put a line in every future prompt with no way out but editing the file
by hand, and a fact that is true today stops being true.
2026-08-13 15:13:32 -04:00
Gmer4Lfe e64d26af6c Put each page's settings on that page
A watchdog threshold was reachable only from the bottom of a list of a hundred
and twenty on another tab.
2026-08-12 19:55:51 -04:00
Gmer4Lfe 8ca52c64b9 Give the rest of the conf a place to be changed
A third of the settings had no control anywhere, so the answer to "where do I
change this" was to open a file over SSH.
2026-08-12 19:41:01 -04:00
Gmer4Lfe a04368e9fc Notice when the question is about their own machine
The other detector recognises named components, so its complement is unbounded;
this one reads grammar instead, which is not.
2026-08-12 18:08:50 -04:00
Gmer4Lfe 533328aa91 Remember whether it may look things up
The reasoning checkbox beside it was remembered and this one was not, so ticking
it, closing the panel and coming back read as a save that had failed.
2026-08-12 17:42:31 -04:00
Gmer4Lfe f8c4c93f20 Let it look things up rather than putting it in search mode
Results announced themselves as the source, so a question it already knew got
answered by paraphrasing the first hit.
2026-08-12 17:32:41 -04:00
Gmer4Lfe e2b304daad Stop proposing to remember what the UI map already says
The map documents 447 settings, so without this every question about where a
control lives earns a proposal that costs a dismissal and teaches nothing.
2026-08-12 17:26:26 -04:00
Gmer4Lfe 20c17648e6 Require the registries this renderer's callers read
The AI tab included this without confform.php and referenced a constant from
it, which is a fatal, which is a blank page.
2026-08-12 17:09:03 -04:00
Gmer4Lfe 240876e710 Tell the assistant where the settings are, not just what they are called
The index reads tracked files and page markup is not one, so it could name a
conf key and never say there was a button for it.
2026-08-11 22:07:10 -04:00
Gmer4Lfe 67510b8fad Draw the Scheduler's settings with the same renderer as everything else
It drew its own, so 329 of its 704 fields were text boxes that should have been
switches, numbers or lists, and 48 credentials rendered legibly.
2026-08-11 20:47:49 -04:00
Gmer4Lfe 24b526db93 Report a child's state from the list it was rendered from
Only the array name was taken from the orchestrator; the enabled flag still came
from the first-match map, so every orchestrator showed the first list's state.
2026-08-11 20:11:14 -04:00
Gmer4Lfe dfe4c0d495 Toggle the script in the orchestrator you clicked, not the first one that lists it
docker_update.sh runs bare in daily, --weekly in weekly and --remainder in
monthly, so switching it off in monthly disabled the daily run and reported
success.
2026-08-11 20:08:32 -04:00
Gmer4Lfe 2b933c45dd Give every AI setting a control, and every page one set to draw it with
The AI tab could show three per-turn checkboxes and no configuration at all;
the forty-three keys behind it were editable only by hand.
2026-08-11 19:03:12 -04:00
Gmer4Lfe a95def9139 Let the conf say which control each setting deserves
The comments above a setting already state its choices, units and bounds, so the
form can read them instead of asking for the file to be annotated first.
2026-08-11 18:17:04 -04:00
Gmer4Lfe 6855dcb8fd Watch reasoning in the same control you read it in afterwards
A capped window of its own made the block you were watching turn into a
different control the moment the answer arrived.
2026-08-11 17:37:39 -04:00
Gmer4Lfe e3ec54f213 Let each control actually govern the thing it names
Auto Scroll, the learning gate, the fold and the flash label each described a
behaviour they did not enforce.
2026-08-11 17:25:51 -04:00
Gmer4Lfe aa3d92360e Diff a proposed script against the open one and take it a hunk at a time 2026-08-10 22:41:07 -04:00
Gmer4Lfe 66f427dd2d Watch the reasoning as it is written, and hold the view where you put it 2026-08-10 22:33:57 -04:00
Gmer4Lfe 25361fafdb Let a long conversation list be filtered instead of scanned 2026-08-10 22:18:06 -04:00
Gmer4Lfe 081366a682 Fold long answers and offer a way back to the newest line 2026-08-10 22:16:11 -04:00
Gmer4Lfe 29e0daf80d Let the assistant propose what to remember, and the operator decide what is kept 2026-08-10 22:00:42 -04:00
Gmer4Lfe 57f3bf2807 Split memory into assisted and learned, and deny learned the precedence assisted has 2026-08-10 21:50:35 -04:00
Gmer4Lfe 775bdce6e1 Add slash commands, and stop Escape and recall from leaving a stale draft 2026-08-10 21:25:04 -04:00
Gmer4Lfe b9b2a59915 Let the last exchange be retried, edited or dropped, and stop losing drafts to a tab swap 2026-08-10 21:22:06 -04:00
Gmer4Lfe 33affc5a59 Open a cited script in the editor instead of a read-only viewer 2026-08-10 21:15:45 -04:00
Gmer4Lfe 9adf760c8e Give every code block its own copy and insert, and number the long ones 2026-08-10 21:12:15 -04:00
Gmer4Lfe eb24e60b8c Stream the answer as it is written, and let the operator stop it 2026-08-10 20:22:57 -04:00
Gmer4Lfe 9c744b34f8 Offer to drop an assistant's code block straight into the Scheduler editor 2026-08-10 19:59:28 -04:00
Gmer4Lfe c0da7d935d Delimit the subreddit filter with ~ so it actually runs
The pattern used # as its delimiter and excluded a literal # inside the character class, so PCRE
ended the pattern early and the match never fired. It read as the filter doing nothing. The unit
test missed it by asserting against a retyped copy of the regex rather than the one in the file,
which is the only version that matters.
2026-08-09 22:55:29 -04:00
Gmer4Lfe 07f679b2e7 Drop subreddit front pages from degoog results
Its Reddit engine returns a community's landing page for any query at all — a question about ZFS
scrubs came back with Rocket League Esports and Dividend Investing, interleaved one in two, so
half of what reached the model was noise. Threads from that same engine are frequently the best
result there is, so the engine stays and only the URL shape goes: /r/<name> with nothing after it
is a front page by definition. That is a fact about the URL, not an opinion about how well it
matches, and this file does no similarity scoring of its own.
2026-08-09 22:54:42 -04:00