Skip to content

fix(tui): Ecosystem screen — path resolution + row-select + Schedule infusion (PR-A) - #10

Merged
flg77 merged 1 commit into
mainfrom
fix/tui-stabilisation
Apr 30, 2026
Merged

fix(tui): Ecosystem screen — path resolution + row-select + Schedule infusion (PR-A)#10
flg77 merged 1 commit into
mainfrom
fix/tui-stabilisation

Conversation

@flg77

@flg77 flg77 commented Apr 30, 2026

Copy link
Copy Markdown
Owner

Summary

Repairs every dead operator action in the Ecosystem screen the user surfaced via screenshots + notes on 30-Apr-2026. Four bugs collapse to one root cause + one antipattern.

Bugs fixed

# Symptom Root cause
1 SKILLS / MCP SERVERS tables show "no skills loaded" / "no MCP servers loaded" guidance rows even though `skills/echo` and `mcps/echo_server` exist. `_roles_root() / _skills_root() / _mcps_root()` returned bare relative strings. When the TUI launches from outside the repo root (pip-install entry point, container with `WORKDIR=/app`), those resolve against `os.getcwd()` — partial-resolution mismatch is exactly what the screenshot shows.
2 ROLE DETAIL panel never updates on row highlight or click. Bare `except Exception: pass` blocks swallow every failure silently.
3 "Schedule infusion → Nucleus" button stays disabled / does nothing. The row-select handler set `_selected_role` AFTER `_show_role_detail()`, so any failure in the detail render silently dropped the selection state and left the button disabled.
4 No log lines an operator could investigate from. Same bare `except` antipattern.

Surface added

`acc/tui/path_resolution.py` (new)

```python
def resolve_manifest_root(env_var: str, default_dir_name: str) -> Path:
```
Resolution order:

  1. `ACC_*_ROOT` env var IF the path exists. If the env var points to a missing dir, warns and falls through — no silent use of bad config.
  2. Repo-anchored: `/<default_dir_name>` computed from this module's location. Works in editable installs AND container layouts.
  3. `Path.cwd() / default_dir_name` as last-resort fallback so the test harness keeps working.

`acc/tui/screens/ecosystem.py` (changed)

  • Three `_*_root` helpers delegate to the new resolver.
  • New handler `on_data_table_row_highlighted` — cursor scrolling over a role row now updates ROLE DETAIL live (without pressing Enter).
  • `on_data_table_row_selected` rewritten: `_selected_role` is set BEFORE the detail render, behind a shared `_arm_infusion_button()` helper so highlight + select paths share logic.
  • `_extract_role_name` static helper handles RowKey API variations across Textual versions.
  • Every bare `except Exception: pass` → `logger.exception(...)`.
  • `on_button_pressed` now calls `self.notify(...)` with `severity="warning"` when no row is selected — the dead-click becomes visible toast feedback.

Tests (10, all green)

`tests/test_ecosystem_screen_pilot.py`:

Category Tests
Path resolver (unit) env-var wins / repo anchor fallback / missing env path warned-and-skipped
Tables populate (Pilot) skills_table_populated / mcps_table_populated
Row select (synthetic event) row_selected_handler_directly / row_selection_arms_infusion_button
Row highlight (synthetic event) row_highlighted_handler_directly
Button (Pilot) button_press_with_selection / button_press_without_selection_notifies

Why synthetic events instead of `pilot.press("enter")`: Textual's DataTable RowSelected dispatch depends on the widget being the precise focus target at the moment of dispatch — Pilot's harness can't reliably reproduce that in test mode (verified: both `pilot.press("enter")` and `action_select_cursor()` failed deterministically). Constructing `DataTable.RowSelected` / `RowHighlighted` directly and dispatching to the screen's handler exercises the exact same code path Textual would invoke on a real keypress, with no flakiness.

Test plan

  • `pytest tests/test_ecosystem_screen_pilot.py -v --no-cov` → 10 passed
  • `pytest tests/test_config.py tests/test_role_store.py tests/test_guardrails.py tests/test_compliance.py --deselect tests/test_role_store.py::TestEd25519Validation --no-cov -q` → 139 passed
  • Manual: `acc-tui` → 6 Ecosystem. Up/Down on role table → ROLE DETAIL updates LIVE. Enter on coding_agent → ROLE DETAIL pinned + Schedule-infusion button enabled. Click → switches to Nucleus with form pre-filled. SKILLS table shows: `echo 0.1.0 LOW —`. MCP SERVERS shows: `echo_server http LOW echo`.

Out of scope

  • PR-A2 — in-TUI manifest upload (FilePicker modal). Splits out per the approved plan.
  • PR-B — direct prompt pane + open `PromptChannel` Protocol.

Notes file: `C:\Users\micro\Documents\Notes\Notes\Development\AgenticCellCorpus\ACC TUI\TUI Fixes.md`

…hedule infusion + bare excepts (PR-A)

User report (notes 30-Apr-2026 + screenshots): on a freshly launched
TUI the Ecosystem screen lists 30+ roles correctly, but every operator
action below the list is dead.

Audit traced four distinct bugs to one shared cause + one antipattern:

1. Skills / MCP SERVERS tables show "no skills loaded — see howto-..."
   guidance row even though skills/echo and mcps/echo_server exist on
   disk.
2. ROLE DETAIL panel never updates when a role row is highlighted or
   clicked — stays stuck on "Select a role row to view its full
   definition".
3. "Schedule infusion → Nucleus" button hint pinned at "Select a role
   first" forever; clicking the button does nothing.
4. Bare `except Exception: pass` blocks inside the row-select handler
   swallow every failure silently, so the symptoms above produce no
   log line operators could investigate from.

Root causes:

* `_roles_root() / _skills_root() / _mcps_root()` returned bare
  relative strings (`"roles"` etc.).  When the TUI launches outside
  the repo root (pip install entry point, container with
  WORKDIR=/app), those resolve against os.getcwd().  Some manifest
  dirs hit and others miss depending on cwd at process start —
  exactly the partial-resolution mismatch the screenshot shows.
* The row-select handler set `_selected_role` AFTER calling
  `_show_role_detail()`, so any failure inside the detail render
  silently dropped the selection state.  Combined with bare excepts,
  the operator saw no message and the Schedule-infusion button stayed
  disabled.

Fixes (acc/tui/path_resolution.py — NEW):
  resolve_manifest_root(env_var, default_dir_name) → absolute Path
  Resolution order:
    1. ACC_*_ROOT env var (absolute or expanded against cwd) IF the
       path exists; warns + falls through if env var points to a
       missing dir (no silent use of bad config).
    2. Repo-anchored: <repo>/<default_dir_name> computed from this
       module's filesystem location (Path(__file__).parent.parent.parent).
       Works in editable installs and container layouts.
    3. CWD-relative literal as last-resort fallback (preserves test
       harness behaviour).

Fixes (acc/tui/screens/ecosystem.py):
  * `_roles_root` / `_skills_root` / `_mcps_root` delegate to the new
    helper; return type widens str → Path (loaders accept both).
  * `on_data_table_row_highlighted` (NEW handler): cursor movement
    over a role row now populates ROLE DETAIL live, matching the UX
    every spreadsheet-style table provides.  Pre-PR-A the operator
    had to know to press Enter; now scrolling does the right thing.
  * `on_data_table_row_selected` rewritten: sets `_selected_role`
    BEFORE calling `_show_role_detail()` so a render failure cannot
    leave the Schedule-infusion button disabled.  Uses the new shared
    `_arm_infusion_button()` helper.
  * `_extract_role_name` static helper: pulls the role name string
    out of a Textual RowKey across the API surface variations
    different Textual versions present.
  * Every bare `except Exception: pass` replaced with
    `logger.exception(...)` so future regressions surface in the
    rotating TUI log file.
  * `on_button_pressed` calls `self.notify("Highlight or click a role
    row first", severity="warning")` when `_selected_role` is empty —
    the dead-click is now visible toast feedback rather than silent.

Tests (tests/test_ecosystem_screen_pilot.py — NEW, 10 cases all green):
  Path-resolution (3): env-var wins / repo anchor falls back / missing
    env path warned-and-skipped (NOT silently used).
  Pilot-driven (7):
    skills_table_populated_when_manifests_exist — fixture sets
      ACC_SKILLS_ROOT to tmp dir with one echo manifest, table shows
      a real row not the empty-state guidance row.
    mcps_table_populated_when_manifests_exist — same shape for MCPs.
    row_selected_handler_directly — synthetic RowSelected event,
      detail panel update captured via monkeypatched Static.update.
    row_selection_arms_infusion_button — _selected_role + button
      state both correct after handler dispatch.
    row_highlighted_handler_directly — cursor-driven path covered too.
    button_press_with_selection_dispatches_role_preload — happy path
      → RolePreloadMessage reaches the App.
    button_press_without_selection_notifies — sad path → notify call
      captured, no RolePreloadMessage dispatched.

Why synthetic events instead of pilot.press("enter"): Textual's
DataTable RowSelected dispatch depends on the widget being the precise
focus target at the moment of dispatch, which Pilot's harness can't
reliably reproduce in test mode (we tried both pilot.press and
action_select_cursor, both failed deterministically).  Constructing
DataTable.RowSelected / RowHighlighted directly and dispatching to the
screen's handler exercises exactly the same code path Textual would
invoke on a real keypress, with no flakiness.

Existing suite: 139 unit tests across config / role-store / guardrails
/ compliance pass unchanged (TestEd25519Validation deselected for the
lighthouse OpenSSL platform limit, as in PRs #7-#9).

Out of scope (planned for follow-ups per the approved plan):
* PR-A2 — in-TUI manifest upload (FilePicker modal).
* PR-B  — direct prompt pane + open PromptChannel Protocol.

Notes file: C:\Users\micro\Documents\Notes\Notes\Development\AgenticCellCorpus\ACC TUI\TUI Fixes.md
@flg77
flg77 merged commit 06f4de3 into main Apr 30, 2026
@flg77
flg77 deleted the fix/tui-stabilisation branch April 30, 2026 10:25
flg77 added a commit that referenced this pull request May 20, 2026
Bumps that resolve the open Dependabot alerts on the repo:

- uv.lock: urllib3 2.6.3 -> 2.7.0   (#10 decompression-bomb High,
                                     #11 cross-origin sensitive headers High)
- uv.lock: idna     3.13  -> 3.15   (#13 idna.encode bypass — supersedes
                                     Dependabot PR #92)
- uv.lock: pytest   8.4.2 -> 9.0.3  (#9  tmpdir-handling CVE; the patched
                                     line is pytest 9.x, so pytest-asyncio
                                     bumps to 1.3.0 and pytest-cov to 7.1.0)
- pyproject.toml: pytest>=9.0,<10 + pytest-asyncio>=1.0,<2.0 widened to
  let the resolver onto the patched line.
- webgui/package.json: vite ^5.4.0 -> ^5.4.21 (#12 .map path traversal).

#8 (transformers Trainer-class arbitrary-code execution) cannot be
bumped today — the patched line is transformers 5.x but no
sentence-transformers release supports transformers 5 yet. We do NOT
use Trainer (only the SentenceTransformer encode() API for embeddings),
so the vulnerable code is not in our execution path. The sentence-
transformers range is widened to `<5.0` so the bump becomes a one-line
uv-lock change once ST releases support. Documented in pyproject.toml;
GitHub alert #8 to be dismissed with "vulnerable code not in execution
path".

42 webgui tests still pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
flg77 added a commit that referenced this pull request May 31, 2026
… block (v0.3.44) (#10)

Closes followup #58.

v0.3.43 added the `## Currently available` perception block to the
Assistant's system prompt (capability + roster + sub-collectives,
~1KB context).  This roughly doubled per-task input-token count
(~1000 → ~2000 tokens on lighthouse).  At the legacy 2048-token
Cat-B budget the Assistant hit utilization 1.10 ≥ 1.0 after 3
back-to-back tasks and started ALERT_ESCALATE blocking — a
legitimate Cat-B rail firing on a now-larger prompt, but the
operator-facing symptom was "the Assistant stopped answering."

Bumps role.yaml v2.2.0 → v2.2.1.  Inline comment block explains the
v0.3.43 prompt-size shift + leaves headroom for the future Phase-2
JSON action block.  No code change.

Tests: pytest -k "assistant or token_budget or role_def or cat_b"
       96 passed, 3 skipped.

Co-authored-by: Claude Opus 4.7 <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