Skip to content

fix(claude): serialize herdr starts so one session gets one workspace - #1278

Merged
cdalar merged 1 commit into
mainfrom
herdr-start-lock
Oct 27, 2026
Merged

cdalar merged 1 commit into
mainfrom
herdr-start-lock

Conversation

@cdalar

@cdalar cdalar commented Oct 27, 2026

Copy link
Copy Markdown
Owner

Fixes the third cmd/mux.go bug from the review that went with #1275 (#1 and #2 were fixed in #1276).

The bug

Two onctl claude runs starting the same herdr session at once both found no workspace, and each made one. That left:

  • Two Claudes in one directory, under one label.
  • A session that couldn't be attached. ws held two ids, so herdr workspace focus "$ws" failed on every attach. running() still reported true, so retrying never got past it, and the only fix was closing a workspace by hand.

A related race (review finding #4): one run's server restart could close the workspace another run had just made.

Fix

  • Lock: herdr's start script, from the server check to pane run, now runs under flock on /run/boxctl/herdr.lock, which serializes concurrent starts on a box. The lock fd is closed before exec herdr, so attaching doesn't hold it. The background server doesn't inherit it (9>&-).
  • No flock: where flock is missing, the script runs without the lock, as before. It is part of util-linux on Debian and Ubuntu, and busybox has it.
  • Existing duplicates: a label already on two workspaces now attaches to the first instead of failing.

Tests

  • TestHerdrStartConcurrent: two starts race against a slowed-down fake herdr, and exactly one workspace is made.
    • It skips on macOS, which has no flock.
    • I ran it on Linux, cross-compiled and run in a golang:1.26 container: it passed 3 out of 3 runs, and with the lock removed it fails with "2 workspaces made for one session". CI runs on Ubuntu, so it runs there.
  • TestHerdrStartDuplicateLabel: with a label already on two workspaces, the run focuses w1 and makes no new workspace.
    • The fake herdr's workspace focus now rejects unknown ids, as real herdr does. Without that, the test passed even with the fix removed.
  • Checks: go test ./cmd/ passes and golangci-lint reports 0 issues.

Not tested against a real herdr on a box.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Ba6Vc66D5hH1xSTvJcCbVz

Two runs starting the same herdr session at once could both find no
workspace and make one each: two Claudes in one directory under one
label, after which `herdr workspace focus` got two ids and every attach
failed until a workspace was closed by hand. A run that restarted the
server could also close the workspace another run had just made.

herdr's start now runs from the server check to the pane under a flock
on the box (where flock exists), released before attaching and not
inherited by the server. A label already on two workspaces attaches to
the first instead of failing.

Claude-Session: https://claude.ai/code/session_01Ba6Vc66D5hH1xSTvJcCbVz
@codecov

codecov Bot commented Oct 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 50.68%. Comparing base (5b34c3f) to head (2006002).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1278      +/-   ##
==========================================
+ Coverage   50.67%   50.68%   +0.01%     
==========================================
  Files          83       83              
  Lines        9840     9843       +3     
==========================================
+ Hits         4986     4989       +3     
  Misses       4854     4854              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@cdalar
cdalar merged commit c1d7c37 into main Oct 27, 2026
14 checks passed
@cdalar
cdalar deleted the herdr-start-lock branch October 27, 2026 19:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

⌂ 换网址