<feed xmlns='http://www.w3.org/2005/Atom'>
<title>newos4/scripts/newos4-triage.py, 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-11-02T19:31:05+00:00</updated>
<entry>
<title>feat(feedback): chat plane on the dashboard + honest triage ranking</title>
<updated>2026-11-02T19:31:05+00:00</updated>
<author>
<name>Agando</name>
<email>roduccomm@gmail.com</email>
</author>
<published>2026-11-02T19:31:05+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=431cef232d2804c0568ff3f3c1e95caaa6be6870'/>
<id>urn:sha1:431cef232d2804c0568ff3f3c1e95caaa6be6870</id>
<content type='text'>
Tasks 3-8 of the chat-plane plan:
- cron newos4-chat-ingest (0cc4bb4d2f82, */10, script-only, silent when idle)
- collect.py: plan_key() normalises the two ledger spellings on read (5 rows
  carried a plan name without .md and were silently unmatched); per-item
  bulk_pass for the 2026-10-26 pass; counts.chat_total
- feedback.html: 5th tile "from chat"; card shows who closed an item and when,
  with a bulk marker
- triage: UNFILEABLE class -&gt; top_real now ranks actionable errors only
  (first row went from an 18 203 fetch failure to the 989 toLowerCase runtime
  error); network/abort signatures move to policy_noise
- AGENTS.md: the chat plane documented as an intake

Verified: autoreview --dry-run over the 5 chat reports -&gt; "nothing provable
(5 left for the operator)" (R6 refuses auto:false); maintain 16 PASS 0 FAIL;
feedback.html inline JS node --check OK; collect -&gt; awaiting 5, chat_total 5.
</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): 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>
</feed>
