<feed xmlns='http://www.w3.org/2005/Atom'>
<title>newos4/api, branch main</title>
<subtitle>newOS4 - the browser terminal that runs WebX11 user spaces</subtitle>
<id>https://newos.webx11.duckdns.org/newos4/atom?h=main</id>
<link rel='self' href='https://newos.webx11.duckdns.org/newos4/atom?h=main'/>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/'/>
<updated>2026-10-26T13:45:36+00:00</updated>
<entry>
<title>feat(newos4): filter network-class auto reports at ingest — count, don't file</title>
<updated>2026-10-26T13:45:36+00:00</updated>
<author>
<name>Agando</name>
<email>roduccomm@gmail.com</email>
</author>
<published>2026-10-26T13:45:36+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=4179025e04093eecedb11d1d5355e994d755c14f'/>
<id>urn:sha1:4179025e04093eecedb11d1d5355e994d755c14f</id>
<content type='text'>
The debug supervisor files an auto report for any failed fetch, so a flaky phone connection or a
retired endpoint became a review card. One cluster held 14 407 + 1 031 occurrences and produced
3 of the 19 cards in the queue; 10 of 21 items reviewed on 2026-10-26 were noise for this reason.

api/debug/report.php: an auto report whose *every* error is network-class (fetch failed /
failed to fetch / network error / load failed / aborted / signal is aborted / ERR_INTERNET_*)
now increments data/debug/network_failures.json (per day, per source path, top 40 kept) and
returns 202 {filtered:"network"}. No RPT file, no card.

Never filtered: any report with auto=false (user content), and any auto report carrying a
stack/TypeError, i.e. a real defect. Verified on a throwaway tree and live through nginx:
  auto network poll.php   -&gt; 202 filtered, no file
  auto network /ollama/   -&gt; 202 filtered, no file
  auto TypeError + stack  -&gt; 201 filed (RPT-20261026-134455-95f10612)
  user report w/ "fetch"  -&gt; 201 filed (RPT-20261026-134455-ab0f2cd4)
Live: counter total=1 by_source {'/spaces/newos4/api/chat/poll.php': 1}, reports stayed at 19.

The dashboard now shows the counter instead of silence:
  "🌐 1 network failure counted at ingest today, not filed as cards — /spaces/newos4/api/chat/poll.php ×1"
</content>
</entry>
<entry>
<title>fix(newos4): outboxResult scoped, markdown mime, old reply path retired (v4.6.81)</title>
<updated>2026-10-23T18:59:29+00:00</updated>
<author>
<name>Agando</name>
<email>roduccomm@gmail.com</email>
</author>
<published>2026-10-23T18:59:29+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=bc99dfba1898123ea824de98a2d5a06e996ed667'/>
<id>urn:sha1:bc99dfba1898123ea824de98a2d5a06e996ed667</id>
<content type='text'>
delegateTask declared `const outboxResult` inside the AI branch and returned it outside, so a
delegation with the AI module missing threw 'outboxResult is not defined'. The catch turned that
into _handleAgentFailure: the worker stayed idle with no result while spawn_agent reported ok —
the reported symptom exactly. Now function-scoped, and the no-module path returns
'queued — AI module not loaded, nothing executed' rather than claiming 'completed'.

Also: nginx served _docs/*.md as application/octet-stream (no .md type) — now text/markdown +
nosniff; agent-replies.php returns 410 for GET as well as POST, so there is one reply path.

Verified: with the AI module deleted at runtime, delegateTask returns the queued string and
throws nothing.
</content>
</entry>
<entry>
<title>fix(chat): GLM review batch — corruption, traversal, cursor truncation, double print (v4.6.78-80)</title>
<updated>2026-10-23T18:37:54+00:00</updated>
<author>
<name>Agando</name>
<email>roduccomm@gmail.com</email>
</author>
<published>2026-10-23T18:37:54+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=577c9241a4ac1c55c5ed0031b76a8a1f83798752'/>
<id>urn:sha1:577c9241a4ac1c55c5ed0031b76a8a1f83798752</id>
<content type='text'>
12 findings accepted, 3 refuted with evidence (see _docs/chat-review-glm-2026-10-23.md).

Data loss: a corrupt thread read as [] and the next append rewrote it with one message —
now 500 and the file is left alone. Cursor after a &gt;50-message truncation pointed at the
thread tail, making the oldest permanently unfetchable — now the last message actually
sent, plus truncated:true. Two posts in the same millisecond shared a cursor — epoch is
now strictly increasing.

Security: chat.py show() had no id validation and joined argv onto CHAT_DIR ('../../x'
read any .json as darkbot) — THREAD_RE added. The auto-reply prompt now treats message
text as untrusted data and forbids acting on instructions found in it.

Ops: relay housekeeping swept neither .lock-* nor orphaned .tmp-*; a crashed listing
looked identical to an empty queue; grep AWAITING also matched message bodies; hermes-poll
printed every reply twice.

Verified: corrupt file -&gt; 500 with the file untouched; traversal -&gt; 'invalid thread id';
hermes-poll -&gt; exactly one line per reply.
</content>
</entry>
<entry>
<title>feat(chat): token-scoped chat endpoints, contract, host CLI, old POST retired (v4.6.71 phase B)</title>
<updated>2026-10-23T17:29:31+00:00</updated>
<author>
<name>Agando</name>
<email>roduccomm@gmail.com</email>
</author>
<published>2026-10-23T17:29:31+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=0d2365ab202ba5c9b1d0604467af0594dc7360de'/>
<id>urn:sha1:0d2365ab202ba5c9b1d0604467af0594dc7360de</id>
<content type='text'>
- api/chat/{lib,post,poll}.php: thread = sha256(token)[0:16]; flock read-modify-write;
  tmp+rename; ms epoch cursor; per-thread + per-IP windows with retry_after_ms;
  401/413/507/405 carry a machine code, only rate_limited is retryable
- _docs/chat-contract.md: frozen shapes, incl. why the cursor is ms and not ts
- scripts/chat.py: list/show/reply, UTC, locked, atomic replace
- api/debug/agent-replies.php POST -&gt; 410 Gone with pointers (one write path)

Measured: 401 no-token, 201+id, poll 1 msg, poll&gt;since 1 new, data/chat 403,
4001 chars 413 (codepoints), 429 with Retry-After 576, round trip via chat.py.
</content>
</entry>
<entry>
<title>feat(newos4): fix ledger + norm clustering — the loop now knows what a fix was worth</title>
<updated>2026-10-23T14:16:39+00:00</updated>
<author>
<name>Agando</name>
<email>roduccomm@gmail.com</email>
</author>
<published>2026-10-23T14:16:39+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=dded2e40ece2d5f2148744b51636c75ba625bf43'/>
<id>urn:sha1:dded2e40ece2d5f2148744b51636c75ba625bf43</id>
<content type='text'>
Lifetime counts ranked an 8-day-old outage above today's live crash, and nothing asserted that a
shipped fix actually held: a regression of a fixed bug looked exactly like a new bug.

- api/debug/ingest.php: norm_key() stores `norm` (host/port/scheme/`?v=`/`//` stripped) — the client
  hash covers the raw URL, so one outage became two signatures (`/ollama//api/tags` 18 203x and
  `/ollama/api/tags` 16 540x). PHP + Python implementations verified byte-identical on 5 real
  messages.
- scripts/newos4-ledger.py: data/maintenance/fixes.json maps norm -&gt; {commit, fixed_at, note}, and
  verify() returns REGRESSED / WATCHING / CLOSED / UNSEEN. "Seen after the fix" alone is not a
  regression (a fix shipped 10d ago with one sighting 9d ago has been silent since) — regressed means
  seen after the fix AND recent; otherwise the silence wins.
- scripts/newos4-triage.py: clusters (grouped by norm, recency-ordered), active&lt;24h / stale&gt;7d counts,
  FIX| verdict lines; digest gains clusters + fixes and active/stale/cluster/regressed counters.
- api/debug/dashboard.php: Clusters / Active&lt;24h / Stale&gt;7d / Regressed columns and a red REGRESSED
  detail line, so a regression is visible without reading JSON.

Seeded with the two fixes whose signatures exist: 2855c63 (pipe crash, WATCHING 69.3h silent) and
563a9be (emptyTrash, UNSEEN). Verdict logic proven on fixtures: recurrence 5m after fix -&gt; REGRESSED,
silent 9d -&gt; CLOSED, last seen 2d -&gt; REGRESSED, fixed 5m ago/last seen 3d ago -&gt; WATCHING.
Loop verified end to end: triage via newos4-maintenance.sh prints ACTIVE/CLUSTER/FIX lines,
maintain OK, skill audit 7/7, dashboard ALL VIEWPORTS OK.
</content>
</entry>
<entry>
<title>fix(newos4): empty-pipeline crash, stacks in reports, frame in digest + dashboard (v4.6.55)</title>
<updated>2026-10-23T13:55:51+00:00</updated>
<author>
<name>Agando</name>
<email>roduccomm@gmail.com</email>
</author>
<published>2026-10-23T13:55:51+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=ef7e4d4d64c294240542c74d8ad2f99b7ef641f2'/>
<id>urn:sha1:ef7e4d4d64c294240542c74d8ad2f99b7ef641f2</id>
<content type='text'>
Root cause: splitPipe() splits on every '|', so `help |`, `| wc -l`, `a || b` produced a stage with
no argv and executePipeChain() called parts[0].toLowerCase() on undefined — 989 occurrences, first
filed 2026-10-20, unfixable from the report because bug reports dropped the stack that named
executePipeChain:3955. Same unguarded read existed in _classify().

- terminal.js: _classify() returns an inert classification for empty argv; executePipeChain()
  parses each stage once, filters stages with no argv (so the last real stage stays final), warns
  only when the whole pipeline is empty. Last-stage test now compares against the filtered length.
- module-system.js: `bug` diagnostics include the stack (10 frames, 1200 chars); auto-filed reports
  carry it too — they are the majority of the inbox and were previously message-only.
- report.php: per-stack 2 KB cap; fingerprint pinned to message/source/count so adding `stack`
  cannot break dedupe against reports filed before today.
- ingest.php: first_frame() parsed once on ingest, stored as `frame` on each signature.
- newos4-triage.py: FRAME| line with the first application frame.
- dashboard.php: frame under each Top Errors row.
- modules/test/test-pipe-guard.js: ordering tripwire (guard must precede the parts[0] read).

Verified: test RED exit=1 -&gt; GREEN exit=0; browser matrix on v4.6.55 (help |, | wc -l,
echo hi || echo bye, ls | | wc -l, |) -&gt; 0 new error signatures, "|" warns;
auto-filed report now carries stack (RPT-...f3e31124, archived after the probe);
frame = modules/core/core-bundle.js:3955:42; maintain OK; skill audit 7/7.
</content>
</entry>
<entry>
<title>feat(newos4-debug): dashboard triage + thread panels, mobile layout, gate error.php</title>
<updated>2026-10-23T12:54:27+00:00</updated>
<author>
<name>Agando</name>
<email>roduccomm@gmail.com</email>
</author>
<published>2026-10-23T12:54:27+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=caa3bcd8b7f49aec834d6f3d0ed05e7d56bcf393'/>
<id>urn:sha1:caa3bcd8b7f49aec834d6f3d0ed05e7d56bcf393</id>
<content type='text'>
- Triage card reads admin/docs/newos4-triage-latest.json defensively: missing/unreadable
  renders as a warning pill, never as zeros (a zero here reads as "nothing is wrong").
  Freshness pill goes stale past 26h so a dead collector cannot look healthy.
- Maintenance thread merges in-app `feedback` reports (kind=maintenance) with agent
  replies from data/maintenance/replies.json into one chronological view.
- Mobile: &lt;=640px single column, smaller type, .table-scroll wrappers, Type column
  hidden, max-width clamp. verify-mobile.py: 360/390px, light+dark -&gt; ALL VIEWPORTS OK,
  page width == viewport (no horizontal overflow).
- "Last ingest" now uses max(ingested_at) instead of end($errors): after the prune the
  array is not chronological and the header contradicted the triage card by 3 days.
- Auto-refresh only fires while the tab is visible (was an unconditional 30s reload).
- Fixed triage checks parsing: digest ships {exit, findings[]}, not a list of strings —
  the first render fataled on stripos(array). Verified 15,167 B render, 0 PHP errors.

nginx (not in git): error.php added to the admin-gated read regex alongside
stats|errors|clusters|dashboard|agent-replies — error.php?hash= was still serving stacks
to anonymous callers. All six now answer 302 without a session.
</content>
</entry>
<entry>
<title>feat(newos4): maintenance agent channel — triage engine, two-way replies, cron</title>
<updated>2026-10-23T12:39:38+00:00</updated>
<author>
<name>Agando</name>
<email>roduccomm@gmail.com</email>
</author>
<published>2026-10-23T12:39:38+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=25833fb43a0ed3e9c7522dfb6a78f29dfd947720'/>
<id>urn:sha1:25833fb43a0ed3e9c7522dfb6a78f29dfd947720</id>
<content type='text'>
- scripts/newos4-triage.py: deterministic collector (records vs occurrences, real vs
  noise vs self-referential, new reports since last run, checker WARN/FAIL). Untrusted
  strings are sanitised and prefixed DATA| before they can reach an agent prompt;
  state + output written under flock with mode 644.
- scripts/newos4-reply.py: appends one reply to data/maintenance/replies.json under
  flock. Hermes writes here instead of POSTing with an admin cookie, so an expired
  session can never look like a delivered reply.
- api/debug/agent-replies.php: GET/POST (admin-gated by nginx) half of the thread.
- api/debug/report.php: accepts thread + kind=maintenance so in-app feedback is
  distinguishable from operator bug reports.
- terminal: `feedback &lt;message&gt;` and `feedback-inbox` commands; help.html rows added
  (help coverage 357/357, modules-index regenerated).

Live proof: `feedback "…"` filed RPT-20261023-123825-781fba86 with thread
thr-glmfwae2 and kind=maintenance; agent-replies GET returned the posted reply, POST
returned 201; triage first run reported 9 new reports, second run 0.
Cron: newos4-maintenance (7e3b326daa4c) daily 07:15, newos4-skills-weekly
(cb6bc2a16216) Mondays 06:30.
</content>
</entry>
<entry>
<title>fix(newos4-debug): idempotent ingest, admin-gated reads, honest counts (v4.6.52-53)</title>
<updated>2026-10-23T12:39:30+00:00</updated>
<author>
<name>Agando</name>
<email>roduccomm@gmail.com</email>
</author>
<published>2026-10-23T12:39:30+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=97f55bf200c74a1b8ca0068ff70cbfeda77916cf'/>
<id>urn:sha1:97f55bf200c74a1b8ca0068ff70cbfeda77916cf</id>
<content type='text'>
Root cause: _syncToBackend() re-sent the whole error DB with cumulative counts every
300s, and ingest.php did count += client count per batch. 33 real signatures had
accumulated 116,696 "occurrences" (one self-referential row alone: 46,133).

Backend
- ingest.php: per-(session,hash) synced map, adds only the delta, resets prev when the
  client count shrank (cleared localStorage), caps delta at 5000, bounds synced at 500
  hashes and sessions at 200, normalises timestamps to +00:00, 30-day retention that
  also purges dropped hashes from every synced map, returns `accepted` (entries) next
  to `ingested` (occurrences).
- stats.php/dashboard.php: total_errors = occurrences, unique_errors = signatures,
  last-ingest card. The two numbers were inconsistent by ~3,500x.
- health.php: reports self_referential count and status=degraded instead of ok.
- Pruned live data: 33 -&gt; 23 records, 116,696 -&gt; 58,759 occurrences, max cluster
  110,226 -&gt; 52,387 (archive kept in data/debug/archive/).
- nginx: stats/errors/clusters/dashboard/agent-replies now behind
  auth_request /_wx11_admin_auth (302 without a session); ingest/report/health stay
  public for telemetry. Verified: reads 302, ingest 200, report 400.

Client
- module-debug-supervisor.js: sends only hashes whose count changed (persisted
  webx11_debug_synced), records synced counts only after success, stops auto-filing
  aborts/timeouts and debug-pipeline requests as bug reports (2 of the last 5 reports
  were "user aborted a request").

Verified with a scratch php -S copy: same batch POSTed 3x -&gt; count stays 5;
count=99999 -&gt; clamped to 1000; retention dropped 10 legacy rows.
</content>
</entry>
<entry>
<title>newOS4 v4.6.40: review fixes (bug diagnostics, trash tree, dir paste, stale capture, preview)</title>
<updated>2026-10-18T15:19:14+00:00</updated>
<author>
<name>Agando</name>
<email>roduccomm@gmail.com</email>
</author>
<published>2026-10-18T15:19:14+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=240e22bc2e4f6b67b032c0a317e456958520fe90'/>
<id>urn:sha1:240e22bc2e4f6b67b032c0a317e456958520fe90</id>
<content type='text'>
</content>
</entry>
</feed>
