From e452ce7785e5e47ec01a409d6da1f59564f8f365 Mon Sep 17 00:00:00 2001 From: Hermes SysOp Date: Thu, 12 Nov 2026 17:02:47 +0100 Subject: fix(newos4): stop reporting network and VFS size as facts we do not have (v4.6.161) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. The last two `navigator.onLine`-as-fact payloads - module-aiassist.js: the debug/health payload carried `online: navigator.onLine`, consumed by downloads/dashboard.html, which rendered "🔴 Offline" — on an Android WebView (reports false while fully connected) that told working users they were offline. The key is gone; the payload carries `network:'unknown'`, `networkKnown:false` and `networkHint`. - module-world-model.js: the snapshot had the same field, and it is also handed to the LLM through `_modelDigest()`/`ask()`, so the model was told the user was offline. Now `network:'unknown'` + `networkKnown:false`. - `_loadFromVFS()` scrubs `system.online` from a model persisted by an older build — otherwise the stale boolean would survive inside the digest for the life of the VFS. - downloads/dashboard.html: `networkText()` renders reachable/unreachable/http-* only when doctor produced them ("(doctor)"), `unprobed` as "— not probed", and everything else as "— unknown (run doctor for a live probe)". A legacy payload's `online:false` lands in "unknown", never "Offline". The row label is "Network", not "Online". - Neither payload invents a probe: SystemModule stores no last-doctor verdict, so 'unknown' is the honest value until one exists. 2. `vfsSize` in both payloads was a different unit again - aiassist: `JSON.stringify(fs.root).length / 1024`; world-model: `JSON.stringify(fs.root).length`. Same lie v4.6.160 fixed in `vfs-size` — UTF-16 code units plus serialisation structure. Both now sum UTF-8 bytes of file content (`_utf8Len`/`_measureVfs`, Blob → TextEncoder → code-point fallback, depth/node capped) and carry `sizeBytes` beside the human string so the rows reconcile. - world-model's `_gatherFileInfo().size` used a THIRD unit on an adjacent line (`JSON.stringify(fs.root).length/1024`); now the same measurement, plus `sizeBytes`. - `showModel()` renders exact bytes and tolerates a pre-fix persisted string. 3. Predictor made a guaranteed-401 request on every boot (UI-plan finding F6) - `_wdConfig()` returns `user:''`/`pass:''` when the saved WebDAV config has a URL but no credentials, and `_pullFromServer`/`_pushToServer` fetched the auth-gated `/spaces//_data/predictor.json` anyway — 107 × 401 in one day, logged by the browser as console errors. Both now refuse without credentials (`cache-only (no credentials)`). Verified: 42/42 node tests, 2 new files (38 payload/honesty assertions, 8 predictor-store assertions); the predictor suite is 4/8 against the pre-fix source, and the payload suite fails 14 assertions against HEAD (`online:false` present, vfsSize === JSON.stringify length). Live Chromium on v4.6.161, on the served dashboard: a legacy payload with `online:false` renders "Network — unknown (run doctor for a live probe)", the current payload renders "85.8 KB (87880 B)" and the same unknown, and a doctor-produced `unreachable` renders "⚠️ unreachable (doctor)". Served bytes identical for core-bundle.js, main.html and downloads/dashboard.html. `maintain.py --fix` all PASS. --- modules/core/module-predictor.js | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'modules/core/module-predictor.js') diff --git a/modules/core/module-predictor.js b/modules/core/module-predictor.js index b534d34..416b06b 100644 --- a/modules/core/module-predictor.js +++ b/modules/core/module-predictor.js @@ -444,6 +444,10 @@ class PredictorModule { async _pushToServer() { const cfg = this._wdConfig(); if (!cfg) { this._storeMode = 'cache-only (no WebDAV config)'; return false; } + // The store is a /spaces//_data path, i.e. auth-gated. A config without credentials + // produces a guaranteed 401 on every boot (measured: 107 × 401 on predictor.json in one day) + // and that 401 is what the console reports as an error. No credentials, no request. + if (!cfg.user || !cfg.pass) { this._storeMode = 'cache-only (no credentials)'; return false; } try { const body = this.terminal.fs.readFile('/system/metrics/predictor.json') || '{}'; const put = () => fetch(this._storeUrl(cfg), { method: 'PUT', headers: this._storeHeaders(cfg), body: body }); @@ -466,6 +470,9 @@ class PredictorModule { async _pullFromServer(force) { const cfg = this._wdConfig(); if (!cfg) { this._storeMode = 'cache-only (no WebDAV config)'; return false; } + // Same rule as the push: an auth-gated path read without credentials is a 401, not a + // "server (empty)" answer, and it costs a console error on every page load. + if (!cfg.user || !cfg.pass) { this._storeMode = 'cache-only (no credentials)'; return false; } if (!force && this._cacheStamp && (Date.now() - this._cacheStamp) < this._STORE_TTL_MS) return false; try { const resp = await fetch(this._storeUrl(cfg), { headers: this._storeHeaders(cfg) }); -- cgit v1.2.3