feat(tui/ecosystem): roles/ watcher + advisory selection lock (proposal 003 PR-3) - #56
Merged
Conversation
Implements PR-3 of proposal 003 (ACC TUI usability hardening).
Addresses operator review item 4: roles directory should be
observed for external file edits; selected roles should be lockable.
## Watcher
A new background asyncio task (`_watch_roles_loop`) on the
Ecosystem screen polls the roles/ tree at a configurable interval
(default 2 s; override via `ACC_TUI_ROLE_WATCH_INTERVAL_S`) and
diffs a cheap fingerprint of role names + per-file mtimes against
the last observed value. On change, posts a new
`RolesChangedMessage` to the Textual bus.
The screen's `on_roles_changed_message` handler:
- Reloads the role cache via `_load_roles()`.
- Re-applies the current filter substring (preserved, not reset).
- Re-renders the detail pane for the currently-selected role if
it still exists.
- Surfaces a 3-second toast so the operator knows a refresh fired
(helpful for debugging external-edit workflows).
Polling-only (no watchdog) — keeps the dependency surface small,
matches the existing role_loader fallback semantics, portable
across Windows / POSIX. Fingerprint shape is sorted tuple of
``(role_name, role.yaml mtime, role.md mtime)`` so additions,
removals, and modifications all produce distinct values.
``_base`` and ``TEMPLATE`` directories are filtered out (parity
with `list_roles`).
## Selection lock
Selecting a role row (highlight or Enter) now takes an advisory
`filelock.FileLock` on the role's `role.yaml.lock` sidecar.
Released on row change, screen unmount, or process exit.
Failure modes:
- `filelock` import unavailable → no lock, quiet (logged).
- Lock already held (Timeout) → warning toast; operator can still
proceed.
Most external editors (vim, notepad, $EDITOR) ignore advisory
locks, so this primarily protects against two TUI sessions
stomping on the same file simultaneously. Documented in proposal
003 §6 risk row 2.
## Files
* `acc/tui/messages.py` — new `RolesChangedMessage` class.
* `acc/tui/screens/ecosystem.py`:
- Module-level `WATCH_POLL_INTERVAL_S` + `_resolve_watch_interval()`
+ `_fingerprint_roles_dir()` helpers.
- `EcosystemScreen.__init__` — watcher + lock state fields.
- `on_mount` — kicks off the watcher with initial fingerprint
captured synchronously to avoid spurious first-tick events.
- `on_unmount` — cancels watcher + releases the held lock.
- `_watch_roles_loop`, `on_roles_changed_message`,
`_acquire_selection_lock`, `_release_selection_lock` —
new methods.
- Row-highlight + row-select handlers — acquire the lock after
`_show_role_detail()`.
## Tests
13 new cases in `tests/test_ecosystem_screen_pilot.py`:
- `_fingerprint_*` (4) — fingerprint detects new role, mtime
bump, role.md addition, and excludes `_base`/`TEMPLATE`.
- `_resolve_watch_interval_*` (4) — env-var override matrix.
- `test_watcher_repopulates_role_table_after_external_add` —
external file add fires the watcher and re-populates the
DataTable within poll interval × small factor.
- `test_watcher_handler_preserves_filter_substring` — refresh
doesn't clear the operator's active filter.
- `test_row_selection_acquires_filelock` — selecting a row
populates `_selection_lock`.
- `test_lock_released_on_screen_unmount` — `on_unmount` clears
the held lock.
- `test_lock_busy_path_notifies_without_crash` — monkeypatched
Timeout produces a warning toast, no crash.
54/54 green across prompt + ecosystem + coding_agent +
oversight TUI test files.
## CHANGELOG
`[Unreleased]` § Added gains four entries:
- Watcher
- Advisory selection lock
- `RolesChangedMessage` message class
## Manual verification
```bash
ACC_TUI_ROLE_WATCH_INTERVAL_S=1 acc-tui
# Navigate to Ecosystem (key 6).
# In another shell:
# echo '# updated' >> roles/coding_agent/role.md
# Within ~1s the Ecosystem screen pops a toast and the role
# detail pane re-renders if coding_agent is selected.
```
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 13, 2026
flg77
added a commit
that referenced
this pull request
May 13, 2026
All six PRs of proposal 003 (ACC TUI usability hardening) are on main: * #54 (PR-1) — Prompt cancel-on-timeout + CHANGELOG/version bump * #55 (PR-2) — Ecosystem role.md detail + searchable filter * #56 (PR-3) — Ecosystem roles/ watcher + selection lock * #57 (PR-4) — Configuration pane (pane 8) * #58 (PR-5) — Performance + Soma cluster/governance context * #59 (PR-6) — CLI/TUI infuse parity + subrole sibling listing pyproject.toml: 0.2.0-dev → 0.2.0 CHANGELOG.md: [Unreleased] section closed off as [0.2.0] (dated 2026-05-14) with a fresh [Unreleased] header ready for the next cycle. Tag v0.2.0 follows this merge. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
flg77
added a commit
that referenced
this pull request
Jun 10, 2026
… agent-core image (#56) * feat(operator): single-repository image addressing + infra storageClass fix Add optional AgentCorpus.spec.imageRepository: when set, every component is addressed within one repository by tag (<imageRepository>:<component>-<tag>, e.g. quay.io/flg77/acc_images:acc-agent-core-0.1.0) for registries that can only host a single repo. When empty, the legacy <imageRegistry>/<name>:<tag> output is byte-identical. All five image sites now go through one helper, util.ComponentImage. Add optional spec.imagePullSecrets rendered onto every pod for private registries. Also fix infra storage: nats.go/redis.go hardcoded storageClassName="" (disabling provisioning); now use the per-component storageClass when set, else leave it unset so the cluster default applies. Adds RedisSpec.StorageClass and stops nats.go from ignoring NATSSpec.StorageClass. OpenSpec: 20260609-operator-single-repo-images. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * perf(container): slim acc-agent-core image 6.4GB -> 1.7GB sentence-transformers pulled the CUDA build of PyTorch (~2.7GB nvidia/* alone) but the agent only embeds on CPU (LLM inference is remote). Install torch from the CPU wheel index and build multi-stage so gcc/python3-devel stay in the builder. Strip __pycache__ + torch test/include, and set HF/TRANSFORMERS offline so the baked all-MiniLM-L6-v2 model loads without a ~40s HF retry stall. Verified: 1.7GB, no nvidia/CUDA, no gcc, offline 384-dim embedding, import acc.agent + lancedb OK. OpenSpec: 20260609-agent-core-image-slimming. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PR-3 of proposal 003 (ACC TUI usability hardening). Addresses operator review item 4: the TUI should observe the roles/ directory and react to external edits; selected roles should be lockable so two TUI sessions can't trample each other.
Two surfaces
File-watcher
New background asyncio task on the Ecosystem screen polls roles/ at a configurable interval (default 2 s; override via
ACC_TUI_ROLE_WATCH_INTERVAL_S) and diffs a cheap fingerprint of role names + per-file mtimes. On change → posts a newRolesChangedMessageto the Textual bus; the handler reloads the cache, re-applies the current filter substring (preserved across refresh), re-renders the selected role's detail pane, and pops a 3-s toast.Polling-only (no watchdog) — small dep surface, portable Windows/POSIX, matches the existing role_loader fallback.
Advisory selection lock
Selecting a row takes an advisory
filelock.FileLockon the role'srole.yaml.locksidecar. Released on row change, screen unmount, or process exit.filelockis already a project dep.Failure modes handled cleanly:
filelockimport fails → no lock, quiet log.Most external editors ignore advisory locks, so this primarily protects against two TUI sessions on the same file — the realistic concurrency hazard.
Files
acc/tui/messages.pyRolesChangedMessageacc/tui/screens/ecosystem.py_fingerprint_roles_dir,_resolve_watch_interval) + watcher loop + change handler + lock acquire/release + lifecycle hookstests/test_ecosystem_screen_pilot.pyCHANGELOG.md[Unreleased]Added section gains four entriesTest plan
Manual verification
Reference
Proposal 003 —
…\ACC Implementation\003 - ACC TUI usability hardening.md(operator vault).🤖 Generated with Claude Code