Summary
The shared per-user diagnostics log ($XDG_STATE_HOME/mecatl/mecatui.log) is
single-writer, and when a second concurrently-running mecatui process can't
acquire the lock, its entire diagnostics stream is silently discarded — no
error, no fallback file, no indication anywhere that logging is a no-op for
that instance.
Where
cmd/mecatui/diaglog.go, openDiagLogWriterWithRetainer:
lock := flock.New(path+".lock",
flock.SetFlag(os.O_CREATE|os.O_RDWR|syscall.O_NOFOLLOW),
flock.SetPermissions(0o600),
)
locked, err := lock.TryLock()
if err != nil || !locked {
_ = lock.Close()
return io.Discard, noop, false
}
TryLock() is non-blocking. If another mecatui already holds
mecatui.log.lock, this call returns io.Discard as the writer — silently.
There's no retry, no fallback to a sibling/PID-suffixed file, and nothing
surfaced to the user or written anywhere that logging was disabled for this
run.
Reproduction
Confirmed empirically: with several mecatui instances already running
against the same workspace, I launched a new one (PID 52704) and, after it
had fully started, checked lsof -p 52704. It had opened zero file
handles matching mecatui.log or mecatui.log.lock — consistent with
TryLock losing the race and falling back to io.Discard without ever
attempting to open the file.
Why this matters
It's easy to accumulate several mecatui processes over time (leaving
sessions open across projects/terminals, forgetting to exit one before
starting another) — in the case that led to filing this, there were 13
concurrent mecatui processes against the same workspace, some running for
multiple days. Only whichever one won the lock race writes to the shared
log; every other instance's diagnostics — including the one you're actually
trying to debug — vanish with no indication that anything was suppressed.
This directly cost real debugging time: while investigating a separate
startup-latency issue (#1695), the shared log
appeared to have "nothing" for the slow run, which looked like evidence
against several otherwise-correct hypotheses. The actual cause only became
visible once we passed --diagnostics-log <private path> to bypass the
shared, contended log entirely.
Suggested direction (not prescriptive)
When the shared log's lock can't be acquired, fall back to a PID-suffixed
sibling file (e.g. mecatui.<pid>.log) instead of io.Discard, so no
instance's diagnostics are silently lost — worst case, an operator now has to
check more than one file, which is strictly better than checking one file
that might be empty for reasons unrelated to the run itself.
🤖 Generated with Claude Code
Summary
The shared per-user diagnostics log (
$XDG_STATE_HOME/mecatl/mecatui.log) issingle-writer, and when a second concurrently-running
mecatuiprocess can'tacquire the lock, its entire diagnostics stream is silently discarded — no
error, no fallback file, no indication anywhere that logging is a no-op for
that instance.
Where
cmd/mecatui/diaglog.go,openDiagLogWriterWithRetainer:TryLock()is non-blocking. If anothermecatuialready holdsmecatui.log.lock, this call returnsio.Discardas the writer — silently.There's no retry, no fallback to a sibling/PID-suffixed file, and nothing
surfaced to the user or written anywhere that logging was disabled for this
run.
Reproduction
Confirmed empirically: with several
mecatuiinstances already runningagainst the same workspace, I launched a new one (PID 52704) and, after it
had fully started, checked
lsof -p 52704. It had opened zero filehandles matching
mecatui.logormecatui.log.lock— consistent withTryLocklosing the race and falling back toio.Discardwithout everattempting to open the file.
Why this matters
It's easy to accumulate several
mecatuiprocesses over time (leavingsessions open across projects/terminals, forgetting to exit one before
starting another) — in the case that led to filing this, there were 13
concurrent
mecatuiprocesses against the same workspace, some running formultiple days. Only whichever one won the lock race writes to the shared
log; every other instance's diagnostics — including the one you're actually
trying to debug — vanish with no indication that anything was suppressed.
This directly cost real debugging time: while investigating a separate
startup-latency issue (#1695), the shared log
appeared to have "nothing" for the slow run, which looked like evidence
against several otherwise-correct hypotheses. The actual cause only became
visible once we passed
--diagnostics-log <private path>to bypass theshared, contended log entirely.
Suggested direction (not prescriptive)
When the shared log's lock can't be acquired, fall back to a PID-suffixed
sibling file (e.g.
mecatui.<pid>.log) instead ofio.Discard, so noinstance's diagnostics are silently lost — worst case, an operator now has to
check more than one file, which is strictly better than checking one file
that might be empty for reasons unrelated to the run itself.
🤖 Generated with Claude Code