CODE-057: concurrent bee index runs share w<i>/ and kill each other's workers

Now
OPEN
Sev
MED
Rep
///bee
Ask
gritzko
Rev
///bee#8cb69a0
See
BEE-072

Input

Context

index/fork.js:77:A9-85 names the worker lanes w<i>/ under one be/ and :199 sweepStale clears them before spawning, so two concurrent runs on one repo reproducibly print bee: index worker 0 failed (Error: cannot commit the index), its files are re-indexed here; with different --fork n the stripe residues differ and a fold can accept a foreign stripe as its own. io.reap at :199 is uncaught, so one failed reap throws out of scan with children unreaped and no marks. index/lindex.js:377:PS foldWork swallows a fold failure (catch (e) { continue; }) AFTER fork.js:206 counted the stripe done, so the marks land over missing rows. Found in the 2026-09-18 index review.

Goals

Name the worker dirs by pid (w<pid>-<i>) or take an O_EXCL lock in be/; wrap the reap; make fold report failure back so the stripe joins rerun.

WIP

Design decisions

The dir is named by the PARENT's pid, w<pid>-<i>: a lock in be/ would serialize two runs that have no reason to wait for each other, and the pid needs no new runtime leaf. The pid is read ONCE at module load, so a forked child inherits it and names the dir its parent will fold. sweepStale now sweeps only the dirs whose run is gone, read off /proc/<pid> the way cpus() reads /sys; where /proc is absent nothing is swept, which wastes a dir and never takes a live worker's lane.

TODOs

Blockers and bummers

A fold failure cannot be injected at the lindex level: read mode abc.index opens a missing dir, an empty dir and a dir that is a plain FILE alike, all with count == 0. So the swallow in foldWork is simply gone and the rerun is covered through the fork.run callback instead. A garbage run file in a worker dir opens as count == 1 and would be merged as rows — silent corruption, no throw. Out of this ticket's scope; worth a ticket of its own.

Outcome