Raised by CodeRabbit on PR #1041 (the develop -> main promotion PR carrying #1040/#1042), declined there as out of scope for that fix chain per Pieter's explicit call, tracked here for a deliberate follow-up.
Current state
host-setup/menu.sh's fetch_hub() holds an flock on $DIR/hub.lock only for the duration of the clone/fetch/checkout sequence (see fetch_hub/fetch_hub_locked in the current file), then releases it. This correctly prevents two concurrent menu.sh sessions sharing the same --dir from racing each other's fetch.
The gap
It does not protect the tree once fetched. A second session's own fetch_hub() call, once it acquires the (now-free) lock, unconditionally does rm -rf "$DIR/hub" before re-cloning — even if a first session is still actively using that same tree for a host or hub task after its own fetch_hub() already returned. Two concrete failure shapes:
- Session A fetches, releases the lock, then runs a long task (e.g.
spec/audit.py) against $HUB_ROOT. Session B starts, fetches (acquiring the free lock), and deletes/replaces the tree A is still reading from.
- Session A finishes and its
cleanup() (on exit, unless --keep) removes $DIR/hub — but if session B fetched a fresh tree there in the meantime, A's cleanup deletes B's tree instead of its own.
Possible directions (undecided)
- Extend the lock's scope to cover the whole fetch-through-cleanup lifecycle, at the cost of serializing all concurrent
menu.sh usage sharing a cache directory (even for unrelated tasks).
- Give each invocation its own isolated cache subdirectory (e.g. keyed by PID or a generated id) instead of one shared
$DIR/hub, trading away the "--keep and reuse across invocations" convenience for concurrency safety by construction.
Neither was chosen; this issue is to make that call deliberately rather than reactively at the tail of an unrelated fix chain.
Raised by CodeRabbit on PR #1041 (the develop -> main promotion PR carrying #1040/#1042), declined there as out of scope for that fix chain per Pieter's explicit call, tracked here for a deliberate follow-up.
Current state
host-setup/menu.sh'sfetch_hub()holds anflockon$DIR/hub.lockonly for the duration of the clone/fetch/checkout sequence (seefetch_hub/fetch_hub_lockedin the current file), then releases it. This correctly prevents two concurrentmenu.shsessions sharing the same--dirfrom racing each other's fetch.The gap
It does not protect the tree once fetched. A second session's own
fetch_hub()call, once it acquires the (now-free) lock, unconditionally doesrm -rf "$DIR/hub"before re-cloning — even if a first session is still actively using that same tree for a host or hub task after its ownfetch_hub()already returned. Two concrete failure shapes:spec/audit.py) against$HUB_ROOT. Session B starts, fetches (acquiring the free lock), and deletes/replaces the tree A is still reading from.cleanup()(on exit, unless--keep) removes$DIR/hub— but if session B fetched a fresh tree there in the meantime, A's cleanup deletes B's tree instead of its own.Possible directions (undecided)
menu.shusage sharing a cache directory (even for unrelated tasks).$DIR/hub, trading away the "--keep and reuse across invocations" convenience for concurrency safety by construction.Neither was chosen; this issue is to make that call deliberately rather than reactively at the tail of an unrelated fix chain.