v0.51.70 — Release AS (this batch):
- PR #2337 (compression snapshot runtime-clear branch 2)
- PR #2334 (turn-journal fcntl lock)
- PR #2342 (INFLIGHT reattach pending user row)
- PR #2339 (workspace panel edge reopen toggle)
v0.51.69 — Release AT (retroactive — these PRs shipped at v0.51.69
tag yesterday but were never moved out of Unreleased at release time;
restoring proper attribution):
- PR #2332, #2333, #2322, #2326, #2327, #2328, #2330, #2331
CHANGELOG drift detected via Pitfall 6 in test-augmentation pitfalls
doc — Unreleased section contained 8 orphan PRs that shipped at the
v0.51.69 tag but were never sectioned correctly. Retroactively splicing
the v0.51.69 header to attribute them properly so future release notes
don't mis-attribute work to v0.51.70.
Replace the hardcoded Skyly cancellation wording with the configured bot_name from settings, falling back to Hermes when unset.
Keep the client-side fallback in sync by using window._botName if the session refresh after cancellation fails.
Co-authored-by: Obryn 🐉 <obryn-ai@dotbeeps.dev>
Opus stage-360 review caught that the docstring at api/streaming.py:40-43
said 'around the entire agent run' which is no longer accurate after the
narrow-lock refactor. The lock is now held only briefly for the env-mutation
critical section; the agent runs outside the lock and the finally block
re-acquires to atomically restore env vars.
Docstring now points to both narrow-lock implementations as references:
- _run_agent_streaming at line ~2719 (the original pattern)
- profile_env_for_background_worker at api/profiles.py:715 (added stage-360)
#2299 added test_env_lock_importable_from_streaming asserting reentrance,
which contradicts the architectural invariant enforced by QA
test_env_lock_is_non_reentrant. The QA test wins because the non-reentrant
property is what makes _ENV_LOCK catch deadlock bugs early.
Updated the new test to assert NON-reentrance to match the actual lock
type (threading.Lock) and the QA invariant.
#2299 introduced profile_env_for_background_worker() in api/profiles.py and
changed _ENV_LOCK from threading.Lock() to threading.RLock(). Both changes
were incorrect:
1. RLock masked rather than fixed the underlying deadlock. The QA
test_env_lock_is_non_reentrant test exists precisely to enforce
non-reentrance — RLock would let a single thread hold _ENV_LOCK across
nested critical sections, which hides bugs while still allowing
different-thread races.
2. The original context manager held _ENV_LOCK for the ENTIRE 'yield'
duration, meaning the lock was held for the full background worker's
runtime (title generation, compression, update summary — possibly
many seconds). That blocked ALL other sessions on _ENV_LOCK, which
the QA test_third_message_completes runtime test caught as a timeout
on the third sequential message.
Fix: mirror the narrow-lock pattern from _run_agent_streaming:
- Acquire _ENV_LOCK only for env mutation (set runtime_env + patch
skill modules)
- Release immediately, yield to worker (no lock held)
- Reacquire in finally to restore env + skill modules
Restored _ENV_LOCK back to threading.Lock(). All 20 QA tests now pass,
including test_third_message_completes (was timing out, now 35s).