Repository navigation
Description
Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment on this issue.
Written-by: FlyBot Dev-2
Claimed-by: FlyBot Lead
Claim-Id: dd4eb92fee9e442d8996b3752516122f
Branch: dev/2-linked-state-unreadable
Base: 16d1645
At: 2026-11-04T17:33:38Z
A team file that is a link to a plain file this host cannot read is reported as read through the link, one row above the row that says the read failed.
Reproduction (live main 16d16456, a state.json that is a symlink to a file with mode 000, run as a non-root user):
- The diagnostics report's state line reads
state file: not a plain file (described only; the store reads the team file through the link and the next save replaces the name). - The very next row reads
| Read-only | The Bot team file … could not be read (EACCES). FlyBot does not change it; fix or move the file and reload. |
The two halves contradict each other: the read the state line promises is the read that row says threw EACCES, and the save the state line promises never happens, because readFile(statePath) fails before writable() can become true. The row is right and the state line is wrong.
Cause: stateNotPlain() (core/src/host/diagnostics.js, the stateFile branch) follows a link with stat() and asks only isFile(). A file whose read bit is clear still answers isFile() === true, so it earns the link sentence even though this host may not read it.
Expected: after stat() answers "a plain file", the target's read permission is asked too — access(path, R_OK), which opens nothing and reads no byte — and only a target this host may read keeps the link sentence. A link to a file it cannot read keeps the leaf sentence, not a plain file (nothing is read or written), which matches the Read-only row below it.
Scope: core/src/host/diagnostics.js (one access() call in stateNotPlain, plus the import), one test in core/test/diagnostics.test.js (skipped when the run is root, the way the neighbouring #2364 case is), one changelog fragment. patches/ds-bot/0001-core-fixes.patch and 0007-version-and-tooling.patch are rewritten by node scripts/ds-bot-patches.mjs generate, never edited by hand.
Red-first evidence (clean main 16d16456 with only the new case added): core/test/diagnostics.test.js 58 tests → 57 pass / 1 fail, expected: 'not a plain file (nothing is read or written)', actual: 'not a plain file (described only; the store reads the team file through the link and the next save replaces the name)'.
Duplicate check (live main plus open and closed issues): #2404 wrote the link sentence for a link to a plain file and kept the leaf sentence for a link to a folder or to nothing; #2416 fixed the sentence a link to a file gets when the target is a normal file. Neither asks whether the target can be read, and the live probe above still shows the contradiction. No open issue or open PR names access( in core/src/host/diagnostics.js.
Activity
host: a team file linking to a file it cannot read is still reported …