Compare commits

..

3 Commits

Author SHA1 Message Date
hamza.khyari bc3cd0524e style(tui): apply ruff formatting to integrated TDD steps
CI / lint (pull_request) Successful in 1m8s
CI / build (pull_request) Successful in 58s
CI / quality (pull_request) Successful in 1m22s
CI / typecheck (pull_request) Successful in 1m49s
CI / security (pull_request) Successful in 1m47s
CI / integration_tests (pull_request) Successful in 5m4s
CI / unit_tests (pull_request) Successful in 6m6s
CI / helm (pull_request) Successful in 24s
CI / push-validation (pull_request) Successful in 19s
CI / docker (pull_request) Successful in 1m31s
CI / coverage (pull_request) Successful in 10m44s
CI / status-check (pull_request) Successful in 3s
Refs: #11039
2026-05-14 11:53:30 +00:00
HAL9000 1a5d1f80f4 fix(tui): rename ActorSelectionOverlay._render to avoid shadowing Textual Widget._render
CI / helm (pull_request) Successful in 1m4s
CI / build (pull_request) Successful in 1m15s
CI / push-validation (pull_request) Successful in 1m29s
CI / lint (pull_request) Failing after 1m37s
CI / typecheck (pull_request) Successful in 2m6s
CI / quality (pull_request) Successful in 1m43s
CI / security (pull_request) Successful in 2m8s
CI / integration_tests (pull_request) Successful in 4m14s
CI / unit_tests (pull_request) Successful in 5m15s
CI / coverage (pull_request) Has been skipped
CI / docker (pull_request) Has been skipped
CI / status-check (pull_request) Failing after 3s
Add TDD regression test for issue #11039: verify that
ActorSelectionOverlay does not define its own _render() method.
The method was renamed to _refresh_display() to prevent shadowing
the Textual Widget._render method which caused NoneType crashes
during layout engine calls.
2026-05-14 11:29:48 +00:00
HAL9000 af819b0807 fix(tui): rename ActorSelectionOverlay._render to _refresh_display
The `ActorSelectionOverlay` class inherits from `textual.widgets.Static`,
which already defines a `_render()` method used for rendering widget content.
Our private `_render()` was shadowing that method, causing incorrect repaint
behavior and interfering with Textual's layout pass. This renames the internal
method to `_refresh_display()` while keeping the public API unchanged.\n\nISSUES CLOSED: #11042
2026-05-14 11:29:48 +00:00
7 changed files with 83 additions and 36 deletions
+3 -3
View File
@@ -22,7 +22,7 @@ env:
jobs:
benchmark-regression:
if: forgejo.event_name == 'schedule' && forgejo.event.schedule == '0 2 * * *' || forgejo.event_name == 'workflow_dispatch' && forgejo.event.inputs.run_full_suite == 'false'
if: github.event_name == 'schedule' && github.event.schedule == '0 2 * * *' || github.event_name == 'workflow_dispatch' && github.event.inputs.run_full_suite == 'false'
runs-on: docker
timeout-minutes: 120
container:
@@ -67,7 +67,7 @@ jobs:
- name: Run benchmark regression via nox
env:
NOX_DEFAULT_VENV_BACKEND: uv
ASV_BASE_SHA: ${{ forgejo.event.inputs.base_sha || 'master' }}
ASV_BASE_SHA: ${{ github.event.inputs.base_sha || 'master' }}
run: |
mkdir -p build
nox -s benchmark_regression 2>&1 | tee build/nox-benchmark-regression-output.log
@@ -107,7 +107,7 @@ jobs:
retention-days: 90
benchmark-full:
if: forgejo.event_name == 'schedule' && forgejo.event.schedule == '0 3 * * 0' || forgejo.event_name == 'workflow_dispatch' && forgejo.event.inputs.run_full_suite == 'true'
if: github.event_name == 'schedule' && github.event.schedule == '0 3 * * 0' || github.event_name == 'workflow_dispatch' && github.event.inputs.run_full_suite == 'true'
runs-on: docker
timeout-minutes: 180
container:
+1 -19
View File
@@ -51,26 +51,8 @@ jobs:
- name: Run asv continuous via nox
env:
ASV_BASE_SHA: ${{ steps.hash.outputs.ASV_BASE_SHA }}
ASV_S3_BUCKET: ${{ secrets.ASV_S3_BUCKET }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
ASV_BASE_SHA: ${{ steps.hash.outputs.ASV_BASE_SHA }}
run: |
mkdir -p build/asv/results
# Check whether S3 baseline data exists before running benchmarks
HAS_BASELINE=false
if [ -n "${ASV_S3_BUCKET}" ] && [ -n "${AWS_ACCESS_KEY_ID}" ]; then
python -m pip install -q awscli
aws s3 ls "s3://${ASV_S3_BUCKET}/asv/results/" >/dev/null 2>&1 && HAS_BASELINE=true || true
fi
if [ "${HAS_BASELINE}" = "false" ]; then
echo "Benchmark regression skipped: no S3 baseline results available to compare against."
mkdir -p build/asv/html
exit 0
fi
nox -s benchmark_regression
- name: Archive the results
+11 -9
View File
@@ -53,6 +53,7 @@ Changed `wf10_batch.robot` to be less likely to create files, and
`final_validation_results` fields on the result model, DI container
## [Unreleased]
- **TUI ActorSelectionOverlay render method rename** (#11042): Renamed `ActorSelectionOverlay._render()` to `_refresh_display()` to avoid shadowing the Textual Widget's internal `_render` method. The overlay class inherits from `textual.widgets.Static`, which has its own `_render` implementation used for rendering widget content. Shadowing this caused incorrect repaint behavior and interfered with Textual's layout pass.
- **Fixed `agents actor add --config` crash with nested `actors:` map and `config.actor` combined shorthand** (#11189): The
CLI command `agents actor add --config` now correctly parses spec-canonical YAML using the
@@ -85,6 +86,16 @@ Changed `wf10_batch.robot` to be less likely to create files, and
- **A2A module rename BDD test suite** (#8615): Comprehensive Behave tests validating that the ACP→A2A module rename is complete — verifying all 22 A2A symbols are properly exported, no legacy ACP references remain in `.py` files under `cleveragents.a2a/`, and the module docstring uses current A2A naming. The step definitions include self-contained symbol lookups to avoid cross-scenario dependency failures.
- Fixed `ReactiveEventBus.emit()` exception handler to log the full exception
message (`str(exc)`) and enable traceback forwarding (`exc_info=True`).
Previously the handler logged only the exception type name (e.g.
"ValueError") with no diagnostic detail, making production debugging
impossible. The handler now includes the error message text and full
traceback in the structlog warning entry. Removed `@tdd_expected_fail` tag
from the TDD test so both scenarios run as normal regression guards. (#988)
### Added
- **`pr-review-worker` review-started notification** (#11028): The `first_review`
and `re_review` modes now post a "review started" notification comment to the
PR at the beginning of the review, giving PR authors immediate visibility
@@ -94,15 +105,6 @@ Changed `wf10_batch.robot` to be less likely to create files, and
- **Plan Rollback Command** (#8557): Implemented `agents plan rollback <plan-id> [<checkpoint-id>]` for checkpoint-based plan state restoration in Epic #8493. The command restores a plan's sandbox to the state captured at a given checkpoint, discarding all decisions made after that checkpoint. The checkpoint can be specified as an optional positional second argument or via the `--to-checkpoint` named option. Supports `--yes/-y` flag to skip confirmation prompts and `--format/-f` for output format selection (rich/plain/json/yaml). Included with comprehensive BDD test coverage (>= 97%) and spec-aligned output formatting showing rollback summary, changes reverted, impact analysis, and post-rollback state panels.
### Fixed
- Fixed `ReactiveEventBus.emit()` exception handler to log the full exception
message (`str(exc)`) and enable traceback forwarding (`exc_info=True`).
Previously the handler logged only the exception type name (e.g.
"ValueError") with no diagnostic detail, making production debugging
impossible. The handler now includes the error message text and full
traceback in the structlog warning entry. Removed `@tdd_expected_fail` tag
from the TDD test so both scenarios run as normal regression guards. (#988)
- **Guard cleanup_stale against execute/processing and execute/complete plans** (#11121):
``_create_sandbox_for_plan()`` in ``src/cleveragents/cli/commands/plan.py`` now
skips ``GitWorktreeSandbox.cleanup_stale()`` when the plan is in
+1
View File
@@ -10,6 +10,7 @@
* Rui Hu <rui.hu@cleverthis.com>
# Details
* HAL 9000 has contributed rename of `ActorSelectionOverlay._render` to `_refresh_display` to avoid shadowing Textual Widget internal method (PR #11042).
Below are some of the specific details of various contributions.
+44
View File
@@ -488,3 +488,47 @@ def step_persona_bar_reflects_actor(context: object) -> None:
assert "anthropic/claude-4-sonnet" in bar._text, (
f"Expected actor in persona bar, got: {bar._text}"
)
# ---------------------------------------------------------------------------
# TDD regression: issue #11039 - no _render() shadowing Textual Widget._render
# ---------------------------------------------------------------------------
@then("the overlay should not override Textual Widget._render")
def step_overlay_no_render_override(context: object) -> None:
"""Verify that ActorSelectionOverlay does NOT define its own ``_render``.
The Shadowing Bug (issue #11039): ActorSelectionOverlay once defined
``def _render(self) -> None`` which returned ``None``, shadowing
``textual.widgets.Static._render()`` (which returns a
:class:`textual.strip.Strip`). This caused the Textual layout engine to
crash with ``AttributeError: 'NoneType' object has no attribute
'get_height'``.
The fix renamed the method to ``_refresh_display`` so that the inherited
base-class ``_render`` is untouched and returns a proper renderable.
"""
from cleveragents.tui.widgets.actor_selection_overlay import (
ActorSelectionOverlay,
)
cls = ActorSelectionOverlay
# The overlay should NOT define its own _render in its __dict__.
# If it did, _render found on the class would resolve to this method
# rather than the Textual base-class implementation.
has_own_render = "_render" in cls.__dict__
assert not has_own_render, (
"ActorSelectionOverlay defines its own _render() which shadows "
"Textual Widget._render. This is the bug from issue #11039."
)
@then("the overlay _refresh_display method should be callable")
def step_overlay_refresh_display_callable(context: object) -> None:
"""Verify ``_refresh_display`` exists and is callable after show()."""
assert hasattr(context._overlay, "_refresh_display"), (
"Overlay must have _refresh_display method"
)
refresh = context._overlay._refresh_display
assert callable(refresh), "_refresh_display must be callable"
+18
View File
@@ -194,3 +194,21 @@ Feature: TUI first-run experience with actor selection overlay
And I call _complete_first_run with actor "anthropic/claude-4-sonnet"
Then the registry should contain a persona named "default"
And the persona bar should reflect the new actor
# -----------------------------------------------------------------------
# TDD regression: issue #11039 - no _render() shadowing Textual Widget._render
# -----------------------------------------------------------------------
# The overlay formerly defined its own _render() returning None, which
# shadowed textual.widgets.Static._render and caused layout-engine crashes.
# This scenario ensures the rename to _refresh_display eliminated that bug.
@tdd_issue @tdd_issue_11039
Scenario: ActorSelectionOverlay does not define its own _render method
Given a new ActorSelectionOverlay
Then the overlay should not override Textual Widget._render
@tdd_issue @tdd_issue_11039
Scenario: ActorSelectionOverlay has refresh_display callable instead of render
Given a new ActorSelectionOverlay
When I call show on the overlay
Then the overlay _refresh_display method should be callable
@@ -145,7 +145,7 @@ class ActorSelectionOverlay(_StaticBase):
self._confirmed = False
self._selected_actor = None
self._visible = True
self._render()
self._refresh_display()
def hide(self) -> None:
"""Hide the overlay and clear its content."""
@@ -161,14 +161,14 @@ class ActorSelectionOverlay(_StaticBase):
if not self._filtered_actors:
return
self._selected_index = (self._selected_index - 1) % len(self._filtered_actors)
self._render()
self._refresh_display()
def move_down(self) -> None:
"""Move the selection cursor down by one position (wraps)."""
if not self._filtered_actors:
return
self._selected_index = (self._selected_index + 1) % len(self._filtered_actors)
self._render()
self._refresh_display()
# ------------------------------------------------------------------
# Search / filter
@@ -191,7 +191,7 @@ class ActorSelectionOverlay(_StaticBase):
else:
self._filtered_actors = list(self._actors)
self._selected_index = 0
self._render()
self._refresh_display()
# ------------------------------------------------------------------
# Confirmation
@@ -218,7 +218,7 @@ class ActorSelectionOverlay(_StaticBase):
# Internal rendering
# ------------------------------------------------------------------
def _render(self) -> None:
def _refresh_display(self) -> None:
content = render_actor_selection(
self._filtered_actors,
self._selected_index,