Summary
When doctor reports a stale gbrain-cycle lock, it advises gbrain sync --break-lock — but that command can only address gbrain-sync:* locks. There is no shipped CLI handler for gbrain-cycle locks, so the only way to clear them is direct SQL into gbrain_cycle_locks.
Repro
- Have a dream cycle die mid-run (process crash, OOM, sigkill) so the
gbrain-cycle row stays in gbrain_cycle_locks past its TTL
- Run
gbrain doctor. The stale_locks check correctly detects it and prints:
gbrain-cycle (pid <N> on <host>, age <age>) → gbrain sync --break-lock
- Run the suggested
gbrain sync --break-lock
- Output:
Lock gbrain-sync:default is not held (nothing to break).
- The cycle lock is unchanged, and subsequent
cycle_freshness checks continue to FAIL
Root cause
src/commands/doctor.ts:1670 hard-codes the hint:
const breakHint = source ? `gbrain sync --break-lock --source ${source}` : `gbrain sync --break-lock`;
src/commands/sync.ts:556 scopes --break-lock to sync locks only:
`gbrain sync --break-lock --source ${lockKey.slice('gbrain-sync:'.length)}`
There is no analogous handler for gbrain-cycle (or any other lock prefix).
Tested workarounds that did not work
Suggested fixes
- Doctor: branch the hint based on the lock_name prefix (e.g.
gbrain-cycle → suggest the cycle breaker)
- CLI: ship a handler. Either:
gbrain dream --break-lock (route by command that owns the lock kind), or
gbrain admin break-lock <lock-name> (kind-agnostic)
- Both should reuse the same liveness + force-break semantics as
sync.tss D3 implementation (PID check, TTL window, cross-host safety, --force-break-lock opt-out)
Environment
- gbrain v0.41.14.0
- Hit on
gbrain-cycle after a dream cycle died at 09:00 UTC; lock TTL expired at 09:30 UTC; PID 3396714 was dead per ps -p at 21:30 UTC
- Stale lock blocked all subsequent dream/cycle runs (cycle_freshness FAIL: "last cycled 30h ago")
Related
Summary
When
doctorreports a stalegbrain-cyclelock, it advisesgbrain sync --break-lock— but that command can only addressgbrain-sync:*locks. There is no shipped CLI handler forgbrain-cyclelocks, so the only way to clear them is direct SQL intogbrain_cycle_locks.Repro
gbrain-cyclerow stays ingbrain_cycle_lockspast its TTLgbrain doctor. Thestale_lockscheck correctly detects it and prints:gbrain-cycle (pid <N> on <host>, age <age>) → gbrain sync --break-lockgbrain sync --break-lockLock gbrain-sync:default is not held (nothing to break).cycle_freshnesschecks continue to FAILRoot cause
src/commands/doctor.ts:1670hard-codes the hint:src/commands/sync.ts:556scopes--break-lockto sync locks only:`gbrain sync --break-lock --source ${lockKey.slice('gbrain-sync:'.length)}`There is no analogous handler for
gbrain-cycle(or any other lock prefix).Tested workarounds that did not work
gbrain sync --break-lock— wrong scope (handlesgbrain-sync:*only)gbrain sync --break-lock --source default— samegbrain sync --break-lock --source all— samegbrain dream --break-lock— flag silently dropped; just runs a cycle (same shape as gbrain dream silently drops --source flag, breaking cycle_freshness loop doctor recommends #1452)Suggested fixes
gbrain-cycle→ suggest the cycle breaker)gbrain dream --break-lock(route by command that owns the lock kind), orgbrain admin break-lock <lock-name>(kind-agnostic)sync.tss D3 implementation (PID check, TTL window, cross-host safety,--force-break-lockopt-out)Environment
gbrain-cycleafter a dream cycle died at 09:00 UTC; lock TTL expired at 09:30 UTC; PID 3396714 was dead perps -pat 21:30 UTCRelated