From d5b6e6f12c8c3c4551050f8fc7d19fb78d6b581b Mon Sep 17 00:00:00 2001 From: Agando Date: Fri, 23 Oct 2026 21:35:02 +0200 Subject: fix(newos4): the virtual /sys entry leaked into listings, trees and counts (v4.6.88-90) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- modules/core/virtual-fs.js | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) (limited to 'modules/core/virtual-fs.js') 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; -- cgit v1.2.3