<feed xmlns='http://www.w3.org/2005/Atom'>
<title>newos4/modules/core/module-fs.js, 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-27T15:26:30+00:00</updated>
<entry>
<title>fix(newos4): piped grep searched the working directory instead of its input</title>
<updated>2026-10-27T15:26:30+00:00</updated>
<author>
<name>Agando</name>
<email>roduccomm@gmail.com</email>
</author>
<published>2026-10-27T15:26:30+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=40c4f5ccd9a54b60cad3a3f29c6c9d32d3fbdac5'/>
<id>urn:sha1:40c4f5ccd9a54b60cad3a3f29c6c9d32d3fbdac5</id>
<content type='text'>
The stdin work in 5cf5010 fixed wc/head/tail but left grep alone, and grep's no-operand branch
sweeps the cwd. Measured on v4.6.115: `cat /gdir/in.txt | grep zzz` answered
`other.txt:1: zzz in a cwd file` — a file that was never piped, and `cat in.txt | grep bbb`
reported the match twice (`in.txt` from the cwd sweep plus `__pipebuf__` from the injected file).

- module-fs.js: when terminal._pipeInput is set and no file operand was given, grep searches the
  staged text and prints `&lt;line&gt;: &lt;text&gt;` (stdin has no filename, as in GNU). The cwd sweep stays
  for a bare interactive `grep x`.
- shell.js: _isPipeAware now covers only sort/cut/uniq/expand. grep/head/tail/wc read _pipeInput
  themselves, so injecting the __pipebuf__ file produced duplicate hits and named stdin results.

Verification:
- test-shell-stdin.js 20 asserts (+4 for grep: no cwd match, non-match reports none, match prints
  no filename, -c counts stdin). RED on HEAD: G1/G2/G3 fail with `other.txt:1: zzz in a cwd file`.
- Suite 18/18 files; maintain --fix "node tests 18/18 pass".
- Live v4.6.116 after `cd /gdir` (where other.txt contains zzz):
  `cat /gdir/in.txt | grep zzz` -&gt; (no matches) · `| grep bbb` -&gt; `2: bbb` · `| grep -c b` -&gt; 1 ·
  `| head -n 2` -&gt; aaa,bbb · `| tail -n 1` -&gt; ccc · `| wc -l` -&gt; 3.

Credit: the false-positive grep was reported by the stdin subagent as an aside; re-derived here
against the live terminal before fixing.
</content>
</entry>
<entry>
<title>feat(newos4): the script runner runs scripts — control flow, variables, globs, redirects, stdin</title>
<updated>2026-10-27T15:23:21+00:00</updated>
<author>
<name>Agando</name>
<email>roduccomm@gmail.com</email>
</author>
<published>2026-10-27T15:23:21+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=cd71cc814f0d51c2d71fca4231690228a1e1da83'/>
<id>urn:sha1:cd71cc814f0d51c2d71fca4231690228a1e1da83</id>
<content type='text'>
MAINT-021 and MAINT-017, executed from .hermes/plans/2026-10-27_145500-newos4-script-runner.md.

- shell.js: new block parser (_parseBlocks) and executor (_runInstructions/_runSimple/_runCommand/
  _dispatchOne). `for`/`while`/`if` bodies now run, including one-line `if [ -f x ]; then y; fi`.
  Variables expand through _expandVars ($VAR, ${VAR}, $?, single quotes literal), globs expand
  through _expandGlobs (unquoted args only, per-command), and `&gt;`/`&gt;&gt;` capture the command's output
  into the VFS instead of being passed to the command as text.
- shell.js: a script never answers an unknown command by calling the AI any more. It prints
  `script:&lt;path&gt;:&lt;line&gt;: &lt;cmd&gt;: command not found` and sets $? — the AI fallback stays for typing.
- shell.js: unclosed blocks and bad `for` headers surface as `Script &lt;path&gt; failed: …` (the parse
  used to sit outside the try, so a bad script crashed the runner).
- shell.js: interactive path gets the same treatment — prompt globbing and `echo hi &gt; /f` now work.
  Cause of the asymmetry: the two code paths had separate dispatch.
- shell.js: `_isPipeAware` no longer injects the __pipebuf__ file into wc (it reads _pipeInput), and
  the module registers itself with a versioned URL and calls its own init() — registerModule() does
  not call init(), so the executeCommand wrapper was never installed from the boot bundle.
- module-fs.js: wc/head/tail/grep read piped stdin when no file operand is given; `cat` accepts
  several operands (`cat *.txt`); `ls &lt;file&gt;` names the file instead of printing "(empty)".
- main.html: shell.js removed from the extension list — it is already inside
  critical-ext-bundle.js, and loading it twice produced two ShellModule instances that could
  disagree about their version.

Verification:
- test-shell-script.js 27 asserts (parser, variables, globs, executor, redirect, command-not-found,
  one-line if, parse error). RED on HEAD: `TypeError: shell._parseBlocks is not a function`.
- test-shell-stdin.js 16 asserts (added by a subagent): wc -l/-c, head -n, tail -n over stdin, and
  the file-argument path unchanged.
- Suite 18/18 files; maintain --fix "node tests 18/18 pass".
- Live on v4.6.115, through the real executeCommand:
  `ls /n*.txt` -&gt; /n1.txt · `cat /n*.txt` -&gt; one|two · `echo hi &gt; /r.txt` then `cat /r.txt` -&gt; hi ·
  `cat /probe.txt | wc -l` -&gt; 3 (bare, no __pipebuf__) ·
  `script /run.sh` -&gt; item 1 / item 2 / item 3 / exists / command not found, exit 1, no AI routing.
</content>
</entry>
<entry>
<title>fix(newos4): never overwrite a directory node; ls honours a path after its flags</title>
<updated>2026-10-27T11:32:37+00:00</updated>
<author>
<name>Agando</name>
<email>roduccomm@gmail.com</email>
</author>
<published>2026-10-27T11:32:37+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=9a61780447066919964b66cdbaa35b9165629419'/>
<id>urn:sha1:9a61780447066919964b66cdbaa35b9165629419</id>
<content type='text'>
MAINT-010 and MAINT-016, the two high items the review left open. Both were re-probed
before the change and the new test is red on HEAD (5 passed, 7 FAILED).

- virtual-fs.js rename()/copy(): they assigned dst.dir.content[dst.name] with no destination
  type check, so `mv file /docs` replaced the /docs node and every child vanished while the
  command printed "Moved: …". Refuse when the destination is a real directory node (the
  virtual /sys entry is exempt). rename() also erased the file on `mv a a`, because assigning
  and then deleting the same key removes it; that path is now an explicit no-op.
- module-fs.js mv/cp: a refusal used to surface as "No such file or directory", which sent
  the operator looking for a missing source. The destination is now inspected first and the
  message says "is a directory — refusing to overwrite it".
- module-fs.js ls: the path was only read from argv[0], so `ls -l /work` listed the cwd.
  The first non-flag argument is now the target; the path-first form is unchanged.

Tests: modules/test/test-fs-mv-cp-guard.js (12 asserts) — RED on HEAD (A1-A3, B1, B2, C2, E1),
GREEN now; maintain --fix reports "node tests 7/7 pass". Browser on v4.6.104:
"mv: /vtest: is a directory — refusing to overwrite it", child and source intact,
"ls -l /vtest" lists /vtest (keep.txt), not the cwd.
</content>
</entry>
<entry>
<title>fix(newos4): grep, head and tail take every path argument (v4.6.67)</title>
<updated>2026-10-23T16:32:25+00:00</updated>
<author>
<name>Agando</name>
<email>roduccomm@gmail.com</email>
</author>
<published>2026-10-23T16:32:25+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=b0404b1c9154c3438d0e124d69dc25d9140cfac4'/>
<id>urn:sha1:b0404b1c9154c3438d0e124d69dc25d9140cfac4</id>
<content type='text'>
Packet 1's multi-path ask was only honoured by wc in v4.6.58 — grep read only
args[ai+1] and head/tail only positional[0], so `grep x a b` searched `a`, printed
one file's hits, and exited 0. Same silent drop, three more commands.

- grep: loops every path; multiple files print `file:line: text`, `-c` prints one
  count per file, missing files are reported per file and excluded from the summary
  count; single-file output unchanged
- head: every path, `==&gt; file &lt;==` header when more than one; `head f 40` still works
- tail: every path with headers; a numeric second positional is still the legacy
  `tail &lt;file&gt; &lt;count&gt;`; `-f` warns and follows only the first file instead of
  printing all of them and claiming otherwise

Verified live on v4.6.67:
grep "bug_id" a b        -&gt; a:2 + b:3 + "2 match(es) in 2 file(s)"
grep -c "alpha" a b      -&gt; one count per file
grep "alpha" a missing   -&gt; hits from a, per-file error, "in 1 file(s)"
head -n 1 a b            -&gt; header + first line for each
tail -n 1 a b            -&gt; header + last line for each
tail a 2                 -&gt; legacy count still honoured
tail -f a b              -&gt; warns, follows a only
</content>
</entry>
<entry>
<title>fix(newos4): an invalid grep pattern returns a message, not an exception (v4.6.64)</title>
<updated>2026-10-23T16:25:52+00:00</updated>
<author>
<name>Agando</name>
<email>roduccomm@gmail.com</email>
</author>
<published>2026-10-23T16:25:52+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=9c27d3790b594c94430015675c91ce618c61be1d'/>
<id>urn:sha1:9c27d3790b594c94430015675c91ce618c61be1d</id>
<content type='text'>
Packet 8. `new RegExp(pattern)` sat unguarded in the terminal grep handler and in
VFS find()/grep(), so a bad pattern threw out of the command handler into the error
ledger -- the same ledger packet 2 had to stop trusting.

- module-fs.js grep: compile in try/catch, print
  "grep: invalid pattern: &lt;pattern&gt; (&lt;engine message&gt;)" as a warning and return
- virtual-fs.js find()/grep(): same guard, return [] and stash the message on
  _lastPatternError instead of throwing into the module error wrapper

Verified live on v4.6.64 with the packet's own repro: `grep "/\bug_id\" file` prints
the message, the error counter stays 0 (was: one ledger entry per call), a valid
pattern still matches, and fs.grep('[') returns [] instead of throwing.
</content>
</entry>
<entry>
<title>feat(newos4): agent status says never-scheduled vs waiting; `guides` command (v4.6.63)</title>
<updated>2026-10-23T16:22:30+00:00</updated>
<author>
<name>Agando</name>
<email>roduccomm@gmail.com</email>
</author>
<published>2026-10-23T16:22:30+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=2bc11203b73fe2eb210ce3dd643773e625609c25'/>
<id>urn:sha1:2bc11203b73fe2eb210ce3dd643773e625609c25</id>
<content type='text'>
Packet 6 and 7.

6 — four agents showed `sleeping` and nothing distinguished "waiting for work" from
"never handed a task". Added tasksAssigned/lastTaskAt to the agent record (counted
where a task is actually assigned), and the status line now reads
  idle — NEVER SCHEDULED (0 tasks since 2026-10-23 16:22)
  idle — waiting for work (3 task(s), last 2026-10-23 16:22)
  parked — NEVER SCHEDULED ...
plus "Dispatch: 0/3 agent(s) have ever been given a task" and a warning that
spawning is not dispatching.

7 — /guides/ was reachable by nobody. New `guides` command lists it newest-first
with sizes and the preview/cat hints; help.html gained a Guides nav entry and
section with links to the published guides.

Verified live on v4.6.63: `guides` → 2 entries after seeding two files;
orchestrator-status shows 3x NEVER SCHEDULED on a fresh session (the packet's own
observation), and "waiting for work (3 task(s) ...)" once a counter is set, with
"Dispatch: 1/3".
</content>
</entry>
<entry>
<title>fix(newos4): refuse shell composition, wc takes multiple paths (v4.6.58)</title>
<updated>2026-10-23T15:53:39+00:00</updated>
<author>
<name>Agando</name>
<email>roduccomm@gmail.com</email>
</author>
<published>2026-10-23T15:53:39+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=2eec7a5c7ab9b724a718ece8b1ffc434677068ac'/>
<id>urn:sha1:2eec7a5c7ab9b724a718ece8b1ffc434677068ac</id>
<content type='text'>
Packet 1 and 3 (silent wrong answers from a shell-less terminal).

- detectComposition() in the security gate refuses ; &amp;&amp; || | before dispatch.
  `;` used to fail loudly, but `&amp;&amp;`/`||` folded the right-hand side into the left
  command's argv (wc -l f &amp;&amp; grep -c x f returned "42 42"), and a `|` arriving from
  the AI run_command tool bypassed splitPipe entirely and degraded the pattern to
  empty -- which matches every line. Operators inside quotes are untouched.
- wc accepts multiple paths: one line per file plus a total, per-file errors for
  missing paths. `wc -c a b` used to print only `a` and exit 0.
- help.html: "There is no shell" callout at the top of the command reference,
  plus the v4.6.57/58 changelog entry.

Verified live on v4.6.58 by replaying the exact call sequence used by both paths:
interactive `wc -c a b` -&gt; "14 a / 10 b / 24 total"; `&amp;&amp;` and `;` refused;
`echo "a &amp;&amp; b"` still echoes; AI run_command semantics with `|` and `&amp;&amp;` refused;
single command and multi-file wc still return real output.
</content>
</entry>
<entry>
<title>fix(newos4): predictor arithmetic, deployed-source viewer, ls text output (v4.6.57)</title>
<updated>2026-10-23T15:46:41+00:00</updated>
<author>
<name>Agando</name>
<email>roduccomm@gmail.com</email>
</author>
<published>2026-10-23T15:46:41+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=71d78fa055d00ed51a9067c441d562807ea95404'/>
<id>urn:sha1:71d78fa055d00ed51a9067c441d562807ea95404</id>
<content type='text'>
Acting on the "Request to the newOS4 team" packet (spaces/agando/guides/,
re-verified by its author on 4.6.56). Three verified defects fixed:

Packet 5 (predictor arithmetic) - module-predictor.js
- the persisted field named mape held a SUM while predictor-status divided by the
  paired count: renamed to errorSum, older payloads read through
- errorPct is stored raw; the 500% clamp is now render-only, so a 475x miss stays
  visible instead of being flattened to the ceiling
- predictions are floored at 1 s and marked floored; a floored prediction is
  consumed but never scored as 100% error (a 0 s prediction is not a prediction)
- observations shorter than 1.5 s (aborted/instant runs) are marked unusable and
  no longer train _findSimilar - one 0.079 s run was rounding the next prediction
  to 0 s
- status prints an unpaired and an unscored count alongside the mean

Packet 10 (verification cannot see its own fixes) - module-aiassist.js
- view_module now returns the DEPLOYED source, prefixed with file and version,
  and only falls back to the _mods copy with a "may be stale" banner. The
  unversioned _mods/&lt;Class&gt;.js edit cache used to shadow the deployed file
  forever, so the viewer showed pre-fix code while the bundle was new.
  Proven by seeding a stale _mods/PredictorModule.js and calling the tool.

Packet 1b (ls exits clean with no output) - module-fs.js
- ls rendered only via printClickable(), which writes straight to the DOM, so any
  capture (AI run_command, diagnostics) saw an empty result and reported "empty
  directory". It now always prints a text line and keeps the clickable chips when
  nothing is capturing.

Test: modules/test/test-predictor-accuracy.js 34/34 (new cases Q, R, S).
</content>
</entry>
<entry>
<title>fix(newos4): coreutils flags + loop-guard arg key, both found by the agent's live run (v4.6.49)</title>
<updated>2026-10-21T22:43:24+00:00</updated>
<author>
<name>Agando</name>
<email>roduccomm@gmail.com</email>
</author>
<published>2026-10-21T22:43:13+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=14c3cb36a3b037111a9fbdf8ff6ea32ee62b3be1'/>
<id>urn:sha1:14c3cb36a3b037111a9fbdf8ff6ea32ee62b3be1</id>
<content type='text'>
Agent session surfaced two real defects:

1. The A4 prompt told it to use grep -n / sed -n 'N,Mp' / wc -l / head -N — none of those flags
   parsed: grep treated '-n' as the pattern (6/6 variants failed), wc read '-l' as a filename,
   head looked for a file named '-2', sed had no range parser. Fixed the tools, not just the text:
   grep now accepts -n/-i/-c/-v/-l (line numbers were already printed), wc accepts -l/-w/-c,
   head accepts -N and -n N, sed accepts Np and N,Mp, tail no longer prints a blank line for a
   trailing newline. Prompt re-ranked to the working forms.

2. Loop guard keyed calls by tool name plus the raw arguments field. When arguments was already
   an object (parsed upstream), the key became 'run_command:[object Object]', collapsing distinct
   batched commands into one signature and producing false "called 3x" warnings. Now the key is
   JSON.stringify(arguments) whenever it is not a string (3 sites: sig, _sigR, _sigD).

Verified live v4.6.49: grep -n/-c, wc -l, head -2, sed -n '2,3p', tail -n 1 all correct.
</content>
</entry>
<entry>
<title>fix(newos4): download link uses the user's space; WebDAV share copies verified file links; preview fullscreen+share; files-&gt;WebDAV upload button (v4.6.45)</title>
<updated>2026-10-21T20:34:12+00:00</updated>
<author>
<name>Agando</name>
<email>roduccomm@gmail.com</email>
</author>
<published>2026-10-21T20:34:12+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=586a3126c4cee9d9c6f4713568c693cb8002a77b'/>
<id>urn:sha1:586a3126c4cee9d9c6f4713568c693cb8002a77b</id>
<content type='text'>
- download: URL built from _resolveSpaceName() (config url = logged-in user), not the page space
- WebDAV browser 🔗: shares selected FILES only, URL-encoded, HEAD-verified (200) before claiming success;
  folders offer a tar fallback instead of a guaranteed-403 link
- file manager preview: ⛶ fullscreen + 🔗 copy-link toolbar (was none)
- file manager toolbar: ☁ WebDAV publishes selected/current file via webdav-push
- modules/test/test-download-url.js: 4 PASS
- rebuild bundles, bump v4.6.44 -&gt; v4.6.45 (loader + 3 script tags)
</content>
</entry>
</feed>
