diff options
| author | Agando <roduccomm@gmail.com> | 2026-10-23 21:35:02 +0200 |
|---|---|---|
| committer | Agando <roduccomm@gmail.com> | 2026-10-23 21:35:02 +0200 |
| commit | d5b6e6f12c8c3c4551050f8fc7d19fb78d6b581b (patch) | |
| tree | 991280a07d4b732b80f3c628fea09f0d76f32676 /modules/core | |
| parent | a7ae7212f5171ec5a4883a5772eed754f592a59f (diff) | |
fix(newos4): the virtual /sys entry leaked into listings, trees and counts (v4.6.88-90)
One phantom entry, three symptoms:
- ls() injected 'sys' whenever it was called without a path, so 'cd _agents; ls' advertised a
sys directory that does not exist, and list_files('_agents/sys') answered 'Directory not
found' — the reported false negative. Injection is now gated on dir === root.
- tree() pushed the same virtual name into the walk and dereferenced it, throwing mid-output on
every root listing. Guarded.
- _gatherFileInfo() walked into /sys and counted a synthetic subtree: 146 files / 114 dirs for a
44-file / 15-dir store. Virtual entries are skipped.
Verified: root still lists sys; _agents and skills do not; tree('') completes; world model total
(44) equals an independent walk of fs.root (44), agree: true.
Diffstat (limited to 'modules/core')
| -rw-r--r-- | modules/core/core-bundle.js | 28 | ||||
| -rw-r--r-- | modules/core/module-loader.js | 2 | ||||
| -rw-r--r-- | modules/core/module-world-model.js | 5 | ||||
| -rw-r--r-- | modules/core/virtual-fs.js | 23 |
4 files changed, 45 insertions, 13 deletions
diff --git a/modules/core/core-bundle.js b/modules/core/core-bundle.js index c8a4c2e..f72a20e 100644 --- a/modules/core/core-bundle.js +++ b/modules/core/core-bundle.js @@ -548,8 +548,11 @@ class VirtualFileSystem { size: dir.content[name]._factory ? 0 : (dir.content[name].content ? dir.content[name].content.length : 0), _virtual: !!dir.content[name]._virtual })); - // Inject virtual /sys/ at root level - if ((!path || path === '/' || path === '') && !entries.some(e => e.name === 'sys' && e._virtual)) { + // Inject virtual /sys/ — but ONLY when the directory being listed IS the root. + // The old test was on the argument (`!path`), not on the result: `cd _agents; ls` + // printed a phantom `sys` inside _agents, and a follow-up list_files('_agents/sys') + // failed with 'Directory not found'. The listing lied; the resolver was right. + if (dir === this.root && !entries.some(e => e.name === 'sys' && e._virtual)) { entries.push({ name: 'sys', type: 'dir', size: 0, _virtual: true }); } return entries; @@ -765,8 +768,11 @@ class VirtualFileSystem { size: dir.content[name]._factory ? 0 : (dir.content[name].content ? dir.content[name].content.length : 0), _virtual: !!dir.content[name]._virtual })); - // Inject virtual /sys/ at root level - if ((!path || path === '/' || path === '') && !entries.some(e => e.name === 'sys' && e._virtual)) { + // Inject virtual /sys/ — but ONLY when the directory being listed IS the root. + // The old test was on the argument (`!path`), not on the result: `cd _agents; ls` + // printed a phantom `sys` inside _agents, and a follow-up list_files('_agents/sys') + // failed with 'Directory not found'. The listing lied; the resolver was right. + if (dir === this.root && !entries.some(e => e.name === 'sys' && e._virtual)) { entries.push({ name: 'sys', type: 'dir', size: 0, _virtual: true }); } return entries; @@ -804,9 +810,14 @@ class VirtualFileSystem { const isLast = i === entries.length - 1; const connector = isLast ? '└── ' : '├── '; result += prefix + connector + name + '\n'; - if (dir.content[name].type === 'dir' || dir.content[name]._virtual) { + // The loop pushes the virtual 'sys' name into `entries`, which has no node behind it: + // dereferencing dir.content['sys'].type threw 'Cannot read properties of undefined' + // and `tree` died on the root listing — often after printing a partial tree, which is + // where the disagreeing file counts came from. + const child = dir.content[name]; + if (child && child.type === 'dir') { const ext = isLast ? ' ' : '│ '; - result += this.tree(path + '/' + name, prefix + ext); + result += this.tree((path || '') + '/' + name, prefix + ext); } } return result; @@ -18814,6 +18825,11 @@ class WorldModelModule { const entries = fs.ls(path); if (!entries) return; for (const e of entries) { + // /sys is synthesized by ls() on demand and has no node behind it. Walking into it + // counted a synthetic subtree (114 dirs / 146 files in a store that holds 43 files), + // which is the "the counts disagree" report: two different populations were both + // printed as "files". + if (e._virtual) continue; if (e.type === 'file') fileCount++; else { dirCount++; walk(path === '/' ? '/' + e.name : path + '/' + e.name); } } diff --git a/modules/core/module-loader.js b/modules/core/module-loader.js index 37212f3..95176d9 100644 --- a/modules/core/module-loader.js +++ b/modules/core/module-loader.js @@ -4,7 +4,7 @@ // Core module bundle loaded synchronously in main.html. // This loader only handles version cache-busting, splash, terminal init, and optional ext modules. - const LATEST_VER = 'v4.6.87'; + const LATEST_VER = 'v4.6.90'; const CACHE = '?v=' + LATEST_VER; const stored = localStorage.getItem('webx11_cache_ver'); if (stored !== LATEST_VER) { diff --git a/modules/core/module-world-model.js b/modules/core/module-world-model.js index 54a17e4..6e8f3c3 100644 --- a/modules/core/module-world-model.js +++ b/modules/core/module-world-model.js @@ -203,6 +203,11 @@ class WorldModelModule { const entries = fs.ls(path); if (!entries) return; for (const e of entries) { + // /sys is synthesized by ls() on demand and has no node behind it. Walking into it + // counted a synthetic subtree (114 dirs / 146 files in a store that holds 43 files), + // which is the "the counts disagree" report: two different populations were both + // printed as "files". + if (e._virtual) continue; if (e.type === 'file') fileCount++; else { dirCount++; walk(path === '/' ? '/' + e.name : path + '/' + e.name); } } diff --git a/modules/core/virtual-fs.js b/modules/core/virtual-fs.js index a52a393..e639cba 100644 --- a/modules/core/virtual-fs.js +++ b/modules/core/virtual-fs.js @@ -546,8 +546,11 @@ class VirtualFileSystem { size: dir.content[name]._factory ? 0 : (dir.content[name].content ? dir.content[name].content.length : 0), _virtual: !!dir.content[name]._virtual })); - // Inject virtual /sys/ at root level - if ((!path || path === '/' || path === '') && !entries.some(e => e.name === 'sys' && e._virtual)) { + // Inject virtual /sys/ — but ONLY when the directory being listed IS the root. + // The old test was on the argument (`!path`), not on the result: `cd _agents; ls` + // printed a phantom `sys` inside _agents, and a follow-up list_files('_agents/sys') + // failed with 'Directory not found'. The listing lied; the resolver was right. + if (dir === this.root && !entries.some(e => e.name === 'sys' && e._virtual)) { entries.push({ name: 'sys', type: 'dir', size: 0, _virtual: true }); } return entries; @@ -763,8 +766,11 @@ class VirtualFileSystem { size: dir.content[name]._factory ? 0 : (dir.content[name].content ? dir.content[name].content.length : 0), _virtual: !!dir.content[name]._virtual })); - // Inject virtual /sys/ at root level - if ((!path || path === '/' || path === '') && !entries.some(e => e.name === 'sys' && e._virtual)) { + // Inject virtual /sys/ — but ONLY when the directory being listed IS the root. + // The old test was on the argument (`!path`), not on the result: `cd _agents; ls` + // printed a phantom `sys` inside _agents, and a follow-up list_files('_agents/sys') + // failed with 'Directory not found'. The listing lied; the resolver was right. + if (dir === this.root && !entries.some(e => e.name === 'sys' && e._virtual)) { entries.push({ name: 'sys', type: 'dir', size: 0, _virtual: true }); } return entries; @@ -802,9 +808,14 @@ class VirtualFileSystem { const isLast = i === entries.length - 1; const connector = isLast ? '└── ' : '├── '; result += prefix + connector + name + '\n'; - if (dir.content[name].type === 'dir' || dir.content[name]._virtual) { + // The loop pushes the virtual 'sys' name into `entries`, which has no node behind it: + // dereferencing dir.content['sys'].type threw 'Cannot read properties of undefined' + // and `tree` died on the root listing — often after printing a partial tree, which is + // where the disagreeing file counts came from. + const child = dir.content[name]; + if (child && child.type === 'dir') { const ext = isLast ? ' ' : '│ '; - result += this.tree(path + '/' + name, prefix + ext); + result += this.tree((path || '') + '/' + name, prefix + ext); } } return result; |