v0.14.4 - Real-time TUI Monitor Improvements (2026-02-24)¶
What Changed?¶
This release significantly improves monitor.py, the real-time TUI monitor for OSECHI detector data. The --watch mode now correctly detects file updates, skips pre-existing content on startup, and refreshes the display reliably. The internal architecture was redesigned to cleanly separate file watching, event queuing, and UI rendering into distinct layers.
What's New¶
Redesigned monitor.py with Reliable Watch Mode¶
What it does:
The --watch mode monitors a directory for newly appended JSONL event lines and displays them in real time. Previously, the monitor failed to detect file updates and loaded all existing content on startup. Both issues are now resolved.
How to use it:
# Watch a directory for new detector events (default pattern: events_*.jsonl)
uv run monitor.py --watch 20260224/
# Watch with a custom file pattern
uv run monitor.py --watch 20260224/ --pattern "run_*.jsonl"
# Read from stdin (piped mode, unchanged)
uv run kazunoko read 100 | uv run monitor.py
Key improvements:
- Startup no longer loads pre-existing file content — only lines appended after launch are shown
- File updates are detected reliably using
PollingObserver(fixes macOS FSEvents limitation) - New
--patternoption controls which files are monitored (default:events_*.jsonl) - UI panels refresh correctly when new events arrive
Installation¶
Quick Start¶
# Get the release
git checkout v0.14.4
# Setup
uv sync
# Run monitor in watch mode
uv run examples/monitor.py --watch /path/to/data/directory
What's Different from the Last Version?¶
✅ Added¶
--patternoption formonitor.pyto filter monitored files (default:events_*.jsonl)register_existing_files()method inDirectoryMonitorto skip pre-existing content on startupPollingObserverbackend to reliably detect file changes on macOS
🔧 Changed¶
monitor.pyinternal architecture redesigned into four clear layers:- Layer 1: File watching (watchdog thread) — enqueues events only
- Layer 2:
Queue— single thread boundary between producer and consumer - Layer 3: Textual main thread — drains queue via
set_interval, updates state, refreshes widgets - Layer 4: Widgets — hold reactive variables and render only
DirectoryMonitornow accepts aQueueinstead of a callbackPipeMonitorclass replaced with simpler_read_stdin_into_queue()function- All widget updates consolidated into a single
_refresh_widgets()method
🐛 Fixed¶
--watchmode failed to detect file updates due to macOS FSEvents not delivering events for detector-written files (fixed by switching toPollingObserver)- Startup loaded all pre-existing file content instead of waiting for new lines
_drain_queuewas a syncdefpassed toset_interval, which requiresasync def— caused the queue to never be drained and the TUI to never updatereactive[list]with default equality check prevented re-renders when list contents were equal (fixed withalways_update=True)- Race condition where the watchdog thread directly mutated
Appstate alongside the Textual main thread
Is It Safe to Upgrade?¶
Backward Compatible: Yes
monitor.pyis an example script, not a library API — no downstream code is affected- Stdin pipe mode (
kazunoko read | monitor.py) behavior is unchanged - The
--watchflag works as before, now with correct behavior
Tests Passed¶
- ✅ Builds without errors
- ✅ Commitizen conventional commits validation
- ✅
PollingObserverconfirmed to detect file updates inexamples/20260224/during live detector run - ✅ Pre-existing file content skipped on startup
- ✅ TUI panels update in real time when new events are appended
Release Details¶
- Date: 2026-02-24
- Version: v0.14.4
- Files Changed: 1 (examples/monitor.py)
- Commits: 0b9c6a8, dda0cd0, b271e75, cd8cc34, a997700
Next Steps¶
- Consider exposing
PollingObserverinterval as a--poll-intervalCLI option for performance tuning - Evaluate whether FSEvents can be re-enabled for non-detector file sources as an opt-in