Skip to content

fix(tui/prompt): publish TASK_CANCEL when receive times out (proposal 003 PR-1) - #54

Merged
flg77 merged 1 commit into
mainfrom
tui/prompt-timeout-cancel
May 13, 2026
Merged

flg77 merged 1 commit into
mainfrom
tui/prompt-timeout-cancel

Conversation

@flg77

@flg77 flg77 commented May 13, 2026

Copy link
Copy Markdown
Owner

Summary

PR-1 of proposal 003 (ACC TUI usability hardening) in the operator's Obsidian vault. Hotfix priority — the operator confirmed vLLM keeps generating after the TUI shows a timeout. Silent backend leak.

This PR also introduces repo-level versioning hygiene (CHANGELOG.md + version bump to 0.2.0-dev) so the proposal-003 series has somewhere to record per-PR entries.

What changed — fix

  • acc/tui/screens/prompt.py timeout handler now calls _publish_cancel(task_id) before appending the system entry to the transcript. Operator sees "(cancelled after Ns — no reply; TASK_CANCEL published)" instead of "(timeout after 60s — no reply)".
  • Timeout default raised 60 → 180 s for slow local backends. Configurable via ACC_PROMPT_TIMEOUT_S environment variable; read fresh per receive call via a new _resolve_timeout() helper. Malformed values fall back to default + log a warning.
  • Cancelled-on-timeout task_ids recorded in a 256-entry FIFO (self._cancelled_task_ids) so late TASK_COMPLETE replies can be suppressed downstream via the new _is_cancelled(task_id) predicate.

What changed — repo hygiene (versioning)

  • CHANGELOG.md introduced in Keep-a-Changelog format. Tracking starts at proposal 003; earlier history reconstructable via git log (not back-filled). [Unreleased] section now carries the PR-1 entries; subsequent PR-2..PR-6 of proposal 003 add their own entries here.
  • pyproject.toml version bumped 0.1.00.2.0-dev. We tag v0.2.0 when proposal 003 closes (all six PRs merged).

Tests

Five new pilot tests in tests/test_prompt_screen_pilot.py:

Test Asserts
test_timeout_publishes_task_cancel TASK_CANCEL appears on acc.{cid}.task.cancel with matching task_id
test_timeout_records_cancelled_task_id FIFO entry present; _is_cancelled() true/false correct
test_timeout_transcript_says_cancelled_not_timed_out Transcript reads "cancel"/"task_cancel" not "timed out"
test_resolve_timeout_default / _reads_env / _ignores_garbage / _ignores_non_positive env-var override matrix

22/22 tests green across tests/test_prompt_screen_pilot.py + tests/test_prompt_channel.py + tests/test_oversight_tui_diagnose.py.

Manual verification

ACC_PROMPT_TIMEOUT_S=10 acc-tui    # 10s timeout for fast iteration
# Open Prompt screen → send a prompt to a slow backend → wait > 10s
# Confirm: acc-deploy.sh logs acc-llm shows cancel within 2s
# Confirm: transcript shows "cancelled after 10s — no reply;
#          TASK_CANCEL published"

Reference

Proposal 003 — C:\Users\micro\Documents\Notes\Notes\Development\AgenticCellCorpus\ACC Implementation\003 - ACC TUI usability hardening.md (operator vault).

🤖 Generated with Claude Code

Implements PR-1 of proposal 003 (ACC TUI usability hardening) in
the operator's Obsidian vault.  Also introduces CHANGELOG.md +
bumps the project version to 0.2.0-dev to anchor the proposal
003 development cycle.

## Problem

Operator tested TUI Prompt screen against a local llama backend.
TUI showed "(timeout after 60s — no reply)" but `acc-deploy.sh
logs acc-llm` confirmed vLLM kept generating long past the
operator-visible timeout — the backend never received a cancel
signal, finished its work, and the late TASK_COMPLETE landed on
a screen the operator had moved past.  Silent backend leak +
abandoned work.

`acc/tui/screens/prompt.py:_publish_cancel()` L417–461 already
implements TASK_CANCEL publishing for the `/cancel` slash
command at L376–390.  The timeout handler at L602–615 just
didn't call it.

## Fix

1. Timeout handler now calls `_publish_cancel(task_id=...)`
   before appending the system entry.  Operator sees
   "(cancelled after Ns — no reply; TASK_CANCEL published)"
   instead of "(timeout after 60s — no reply)".

2. `_RECEIVE_TIMEOUT_S` default raised 60 → 180 s.
   Configurable via the `ACC_PROMPT_TIMEOUT_S` environment
   variable, read fresh per receive call via a new
   `_resolve_timeout()` helper.  Malformed values fall back
   to the default + log a warning.

3. Cancelled-on-timeout task_ids recorded in a FIFO
   (`self._cancelled_task_ids`, capped at 256) for late-
   TASK_COMPLETE suppression via the new `_is_cancelled()`
   predicate — surface for future consumers.

## Repo hygiene (versioning)

- Adds `CHANGELOG.md` in Keep-a-Changelog format.  Tracking
  starts at proposal 003; earlier history reconstructable
  via `git log`.
- Bumps `pyproject.toml` from 0.1.0 → 0.2.0-dev.  Tags the
  v0.2.0 cut when proposal 003 closes (PRs 1–6 all landed).
- Every subsequent proposal-003 PR adds entries to the
  `[Unreleased]` section.

## Tests

Five new cases in `tests/test_prompt_screen_pilot.py`:

- `test_timeout_publishes_task_cancel` — forces 50 ms timeout;
  asserts TASK_CANCEL appears with matching task_id.
- `test_timeout_records_cancelled_task_id` — FIFO entry +
  `_is_cancelled()` true/false matrix.
- `test_timeout_transcript_says_cancelled_not_timed_out` —
  operator-visible message reads "cancel" not "timed out".
- `test_resolve_timeout_default` / `_reads_env` /
  `_ignores_garbage` / `_ignores_non_positive` — env-var
  override matrix.

22/22 green across prompt + channel + oversight TUI tests.

## Manual verification

```
ACC_PROMPT_TIMEOUT_S=10 acc-tui
# Prompt screen → send → wait > 10s
# acc-deploy.sh logs acc-llm shows cancel within 2s
# Transcript shows "cancelled after 10s — no reply; TASK_CANCEL published"
```

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@flg77
flg77 force-pushed the tui/prompt-timeout-cancel branch from ab87a3a to 68e6ffc Compare May 13, 2026 20:20
@flg77
flg77 merged commit e7672fc into main May 13, 2026
@flg77
flg77 deleted the tui/prompt-timeout-cancel branch May 13, 2026 21:02
flg77 added a commit that referenced this pull request May 13, 2026
Implements PR-2 of proposal 003 (ACC TUI usability hardening).
Addresses operator review items 1, 2, 3 from `ACC TUI Review.md`:

1. Role detail box now shows the role's narrative `role.md`
   content (was: only `role.yaml`).
2. Role overview is browsable — detail panel split into two
   `Collapsible` widgets (role.md open by default; role.yaml
   collapsed) inside the existing `ScrollableContainer`, so the
   operator can scroll a long role.md without losing the yaml
   surface.
3. Role table gains a filter `Input` above it.  Typing narrows
   to rows whose name / domain / persona contains the substring
   (case-insensitive).  Clearing restores the full list.

## Changes

### `acc/tui/screens/ecosystem.py`

* Compose:
  - Added `Input(#role-filter)` above the role DataTable.
  - Replaced the single `Static(#role-detail-panel)` with a
    `Static(#role-detail-placeholder)` + two Collapsibles:
    `#role-md-collapsible` (Markdown widget `#role-md-content`)
    and `#role-yaml-collapsible` (Static `#role-yaml-content`).
* `_load_roles()` now caches the loaded row tuples in
  `self._all_role_rows` and delegates the initial render to a
  new `_apply_filter("")` so the filter handler can repopulate
  without re-reading disk.
* `_apply_filter(query)` clears + repopulates the DataTable
  keeping only rows whose name / domain / persona contain
  the (case-insensitive) substring.
* `on_input_changed(event)` reacts to keystrokes in
  `#role-filter`; other Inputs get a no-op.
* `_show_role_detail(role_name)` rewritten:
  - Hides the "select a role" placeholder once a role is
    chosen.
  - Reads `roles/<name>/role.md` via the new module-level
    `_read_role_md()` helper; renders it in the Markdown
    widget.  Missing file → friendly placeholder pointing
    operators at the authoring convention (slot 006).
  - role.yaml render unchanged in content, but lands on the
    `#role-yaml-content` Static under its collapsible.
* New helper `_read_role_md(md_path, role_name)` — read-only
  with safe fallbacks for missing / unreadable files.

### Tests

* `tests/test_ecosystem_screen_pilot.py`:
  - `_capture_panel_updates()` updated to tap BOTH the Markdown
    and the yaml Static so prior substring assertions
    ("test_role", "pilot fixture") keep passing.
  - 5 new cases:
    - `test_role_detail_renders_role_md_when_present`
    - `test_role_detail_md_placeholder_when_absent`
    - `test_role_filter_input_narrows_table`
    - `test_role_filter_matches_persona_substring`
    - `test_role_filter_no_match_empties_table`
* `tests/test_coding_agent_tui_pilot.py`:
  - One-line update so the test reads from `#role-yaml-content`
    instead of the now-removed `#role-detail-panel`.

25/25 green across `tests/test_ecosystem_screen_pilot.py` +
`tests/test_coding_agent_tui_pilot.py`.

## Note on CHANGELOG entry

This PR's `[Unreleased]` CHANGELOG entries will be added during
rebase once PR #54 (which introduces CHANGELOG.md) lands.
Skipping it here avoids a guaranteed merge conflict on a file
that doesn't yet exist on main.

## Manual verification

* Open Ecosystem screen → type substring in the filter →
  only matching rows render.  Clear → full list restored.
* Click any role row → role.md renders in the open
  collapsible at the top.  Roles without role.md show the
  authoring-guidance placeholder.
* Click the `role.yaml (raw)` collapsible → raw yaml renders
  identically to the pre-PR-2 single-pane view.

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

Implements PR-2 of proposal 003 (ACC TUI usability hardening).
Addresses operator review items 1, 2, 3 from `ACC TUI Review.md`:

1. Role detail box now shows the role's narrative `role.md`
   content (was: only `role.yaml`).
2. Role overview is browsable — detail panel split into two
   `Collapsible` widgets (role.md open by default; role.yaml
   collapsed) inside the existing `ScrollableContainer`, so the
   operator can scroll a long role.md without losing the yaml
   surface.
3. Role table gains a filter `Input` above it.  Typing narrows
   to rows whose name / domain / persona contains the substring
   (case-insensitive).  Clearing restores the full list.

## Changes

### `acc/tui/screens/ecosystem.py`

* Compose:
  - Added `Input(#role-filter)` above the role DataTable.
  - Replaced the single `Static(#role-detail-panel)` with a
    `Static(#role-detail-placeholder)` + two Collapsibles:
    `#role-md-collapsible` (Markdown widget `#role-md-content`)
    and `#role-yaml-collapsible` (Static `#role-yaml-content`).
* `_load_roles()` now caches the loaded row tuples in
  `self._all_role_rows` and delegates the initial render to a
  new `_apply_filter("")` so the filter handler can repopulate
  without re-reading disk.
* `_apply_filter(query)` clears + repopulates the DataTable
  keeping only rows whose name / domain / persona contain
  the (case-insensitive) substring.
* `on_input_changed(event)` reacts to keystrokes in
  `#role-filter`; other Inputs get a no-op.
* `_show_role_detail(role_name)` rewritten:
  - Hides the "select a role" placeholder once a role is
    chosen.
  - Reads `roles/<name>/role.md` via the new module-level
    `_read_role_md()` helper; renders it in the Markdown
    widget.  Missing file → friendly placeholder pointing
    operators at the authoring convention (slot 006).
  - role.yaml render unchanged in content, but lands on the
    `#role-yaml-content` Static under its collapsible.
* New helper `_read_role_md(md_path, role_name)` — read-only
  with safe fallbacks for missing / unreadable files.

### Tests

* `tests/test_ecosystem_screen_pilot.py`:
  - `_capture_panel_updates()` updated to tap BOTH the Markdown
    and the yaml Static so prior substring assertions
    ("test_role", "pilot fixture") keep passing.
  - 5 new cases:
    - `test_role_detail_renders_role_md_when_present`
    - `test_role_detail_md_placeholder_when_absent`
    - `test_role_filter_input_narrows_table`
    - `test_role_filter_matches_persona_substring`
    - `test_role_filter_no_match_empties_table`
* `tests/test_coding_agent_tui_pilot.py`:
  - One-line update so the test reads from `#role-yaml-content`
    instead of the now-removed `#role-detail-panel`.

25/25 green across `tests/test_ecosystem_screen_pilot.py` +
`tests/test_coding_agent_tui_pilot.py`.

## Note on CHANGELOG entry

This PR's `[Unreleased]` CHANGELOG entries will be added during
rebase once PR #54 (which introduces CHANGELOG.md) lands.
Skipping it here avoids a guaranteed merge conflict on a file
that doesn't yet exist on main.

## Manual verification

* Open Ecosystem screen → type substring in the filter →
  only matching rows render.  Clear → full list restored.
* Click any role row → role.md renders in the open
  collapsible at the top.  Roles without role.md show the
  authoring-guidance placeholder.
* Click the `role.yaml (raw)` collapsible → raw yaml renders
  identically to the pre-PR-2 single-pane view.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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 pushed a commit that referenced this pull request Jun 11, 2026
Bumps [github.com/moby/spdystream](https://github.com/moby/spdystream) from 0.4.0 to 0.5.1.
- [Release notes](https://github.com/moby/spdystream/releases)
- [Commits](moby/spdystream@v0.4.0...v0.5.1)

---
updated-dependencies:
- dependency-name: github.com/moby/spdystream
  dependency-version: 0.5.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.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