Skip to content

feat(tui/ecosystem): roles/ watcher + advisory selection lock (proposal 003 PR-3) - #56

Merged
flg77 merged 1 commit into
mainfrom
tui/ecosystem-role-watch
May 13, 2026
Merged

feat(tui/ecosystem): roles/ watcher + advisory selection lock (proposal 003 PR-3)#56
flg77 merged 1 commit into
mainfrom
tui/ecosystem-role-watch

Conversation

@flg77

@flg77 flg77 commented May 13, 2026

Copy link
Copy Markdown
Owner

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 new RolesChangedMessage to 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.FileLock on the role's role.yaml.lock sidecar. Released on row change, screen unmount, or process exit. filelock is already a project dep.

Failure modes handled cleanly:

  • filelock import fails → no lock, quiet log.
  • Lock already held (Timeout) → warning toast; operator can still proceed.

Most external editors ignore advisory locks, so this primarily protects against two TUI sessions on the same file — the realistic concurrency hazard.

Files

File Change
acc/tui/messages.py New RolesChangedMessage
acc/tui/screens/ecosystem.py Helpers (_fingerprint_roles_dir, _resolve_watch_interval) + watcher loop + change handler + lock acquire/release + lifecycle hooks
tests/test_ecosystem_screen_pilot.py 13 new cases
CHANGELOG.md [Unreleased] Added section gains four entries

Test plan

  • 13 new cases — fingerprint detection (×4), env-var matrix (×4), external-add roundtrip, filter-preservation, lock acquire/release/busy-path (×3).
  • 54/54 tests green across prompt + ecosystem + coding_agent + oversight TUI files.

Manual verification

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.

Reference

Proposal 003 — …\ACC Implementation\003 - ACC TUI usability hardening.md (operator vault).

🤖 Generated with Claude Code

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>
@flg77
flg77 merged commit 369416c into main May 13, 2026
@flg77
flg77 deleted the tui/ecosystem-role-watch branch May 13, 2026 21:33
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant