Skip to content

Improve TUI usability, quit behavior, and resource safety - #11

Merged
harder merged 3 commits into
mainfrom
feat/tui-usability-hardening
Aug 28, 2026
Merged

harder merged 3 commits into
mainfrom
feat/tui-usability-hardening

Conversation

@harder

@harder harder commented Aug 27, 2026 •

Copy link
Copy Markdown
Owner

Summary

This release improves SkillView's visual hierarchy, keyboard behavior, progress feedback, Markdown previews, and resilience during long-running or repeated operations. It also adds explicit resource bounds and lifecycle cleanup so extended TUI sessions remain responsive and memory usage stays predictable.

User-facing changes

Clearer navigation and screen identity

  • Removes the duplicate Skill View label beneath the application header.
  • Gives each workspace a concise contextual title: Discover skills, Installed skills, Review changes, and Doctor — Environment diagnostics.
  • Adds the missing Doctor title so its diagnostics are immediately identifiable.
  • Separates Installed search guidance from keyboard actions and the status legend. Search scope now appears beside the filter, while actions remain in the bottom shortcut bar.

Better progress and feedback

  • Places loading spinners directly beside their associated status text throughout search, inventory, update, install, and change-review flows.
  • Prevents stale search and preview results from replacing newer selections.
  • Disables conflicting update and install controls while operations are active, reducing accidental duplicate work.

Cleaner Markdown previews

  • Keeps the lightweight Terminal.Gui Markdown viewer and disables generated heading prefixes, so skill names render as names instead of being preceded by confusing ## markers.
  • Continues sanitizing remote and untrusted content before it reaches preview, detail, and log panes.

Predictable keyboard behavior

  • Moves quit handling to the application-level keyboard path so Ctrl+Q works even when focus is inside a text field or nested modal.
  • Keeps plain q as a convenient quit shortcut in top-level read-only views without hijacking text entry.
  • Preserves Esc as the back/close action and makes the help/status guidance match the actual behavior.

Small-terminal protection

  • Adds a clear minimum-size guard for terminals smaller than 80×24.
  • Hides and blocks actions that cannot be safely rendered while the guard is active, while retaining the global quit shortcut.

Reliability and resource hardening

  • Bounds subprocess stdout and stderr capture to 1 MiB per stream, drains both streams in fixed-size chunks so newline-free output cannot bypass the limit, and reports truncation rather than allowing unbounded growth.
  • Converts search-agent metadata caching to a thread-safe 512-entry least-recently-used cache.
  • Caps the visible in-app log history at 512 lines and coalesces redraws to reduce UI churn.
  • Makes logger subscriptions disposable, prevents already-snapshotted callbacks from starting after disposal, and ensures file sinks and application-level subscriptions are released during shutdown.
  • Introduces a reusable latest-request gate that cancels superseded preview work and prevents late UI updates after teardown.
  • Links Installed, Changes, Updates, install, and picker work to their owning screen or dialog lifetime.
  • Cancels superseded inventory loads through synchronized cancellation-source ownership, eliminating cancel-versus-dispose races, and consistently routes surviving UI updates through the Terminal.Gui application dispatcher.
  • Documents the new lifecycle, cancellation, cache, process-output, and terminal-size invariants for future contributors.

Validation

  • dotnet build --no-restore — passed with 0 warnings and 0 errors.
  • dotnet test --no-build — 556 tests passed, 0 failed.
  • Native AOT Release publish for osx-arm64 — passed with IL2026, IL3050, and IL3053 promoted to errors.
  • git diff --check — passed.
  • Added unit and ANSI-driver integration coverage for quit behavior, minimum terminal sizes, bounded newline-free stdout/stderr, cache eviction, concurrent logger unsubscription, cancellation-source ownership races, stale-preview cancellation, and superseded screen loads.

Release notes

SkillView now has clearer screen titles, cleaner search guidance, inline progress indicators, improved Markdown previews, and reliable quit shortcuts across views and dialogs. Long-running sessions are safer and lighter thanks to bounded subprocess output, log history, and metadata caching, plus stronger cancellation and cleanup for asynchronous UI work. Terminals below 80×24 now receive an explicit size warning instead of attempting to render unusable controls.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Output capture remains unbounded for newline-free streams, and several cancellation and log-refresh paths contain concurrency races.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR improves TUI navigation, progress feedback, cancellation, lifecycle cleanup, and resource limits.

Changes:

  • Adds contextual titles, global quit handling, terminal-size protection, and inline spinners.
  • Cancels stale asynchronous work and bounds caches, logs, and subprocess output.
  • Expands unit and integration coverage and documents lifecycle invariants.
File summaries
File Description
AGENTS.md Records new lifecycle and resource rules.
README.md Documents revised quit behavior.
agent_docs/tui-pty-testing.md Updates PTY keyboard guidance.
agent_docs/ui-lifecycle-and-resource-bounds.md Adds hardening guidance.
src/SkillView.Core/Bootstrapping/EntryPoint.cs Disposes CLI logging subscription.
src/SkillView.Core/Logging/FileLogSink.cs Manages logger subscription lifetime.
src/SkillView.Core/Logging/Logger.cs Adds disposable subscriptions.
src/SkillView.Core/Subprocess/ProcessRunner.cs Introduces captured-output limits.
src/SkillView.Core/Ui/ChangesTabView.cs Adds cancellable loading and spinner.
src/SkillView.Core/Ui/ContextBarView.cs Displays workspace titles.
src/SkillView.Core/Ui/DoctorTabView.cs Revises quit-key handling.
src/SkillView.Core/Ui/HelpOverlay.cs Updates keyboard help.
src/SkillView.Core/Ui/InstallConfirmModal.cs Adds operation cancellation and locking.
src/SkillView.Core/Ui/InstalledScreen.cs Defers q to global routing.
src/SkillView.Core/Ui/InstallScreen.cs Repositions inline progress.
src/SkillView.Core/Ui/LatestRequestGate.cs Adds latest-request cancellation.
src/SkillView.Core/Ui/RepoSkillPickerModal.cs Hardens install-dialog lifetime.
src/SkillView.Core/Ui/SearchAgentMetadataCache.cs Implements bounded thread-safe LRU storage.
src/SkillView.Core/Ui/SkillViewApp.cs Integrates quit, guard, cancellation, and log bounds.
src/SkillView.Core/Ui/StatusStripView.cs Embeds the main spinner.
src/SkillView.Core/Ui/TabBarView.cs Removes duplicate branding.
src/SkillView.Core/Ui/Tabs/ChangesTabView.cs Adds cancellable inventory loading.
src/SkillView.Core/Ui/Tabs/DoctorTabView.cs Aligns Doctor navigation semantics.
src/SkillView.Core/Ui/Tabs/InstalledTabView.cs Adds cancellation, progress, and clearer guidance.
src/SkillView.Core/Ui/Tabs/UpdatesTabView.cs Adds cancellation and operation locking.
src/SkillView.Core/Ui/TerminalSizeGuardView.cs Adds minimum-terminal protection.
src/SkillView.Core/Ui/TuiHelpers.cs Hides Markdown heading prefixes.
tests/SkillView.IntegrationTests/Ui/SkillViewAppIntegrationTests.cs Covers top-level quit paths.
tests/SkillView.Tests/Logging/LoggerTests.cs Tests subscription disposal.
tests/SkillView.Tests/Subprocess/ProcessRunnerTests.cs Tests output truncation.
tests/SkillView.Tests/Ui/ChangesTabViewTests.cs Updates cancellation-aware loaders.
tests/SkillView.Tests/Ui/ContextBarViewTests.cs Verifies workspace titles.
tests/SkillView.Tests/Ui/InstalledScreenTests.cs Verifies global q routing.
tests/SkillView.Tests/Ui/InstalledTabViewTests.cs Covers revised guidance and loaders.
tests/SkillView.Tests/Ui/LatestRequestGateTests.cs Tests supersession and cancellation.
tests/SkillView.Tests/Ui/SearchAgentMetadataCacheTests.cs Tests capacity eviction.
tests/SkillView.Tests/Ui/SkillViewAppTests.cs Covers titles, hints, and quit detection.
tests/SkillView.Tests/Ui/StatusStripViewTests.cs Tests spinner state.
tests/SkillView.Tests/Ui/TerminalSizeGuardViewTests.cs Tests size thresholds.
tests/SkillView.Tests/Ui/TuiHelpersTests.cs Tests Markdown configuration.
tests/SkillView.Tests/Ui/UpdatesTabViewTests.cs Tests superseded inventory cancellation.
Review details
  • Files reviewed: 39/39 changed files
  • Comments generated: 7
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/SkillView.Core/Subprocess/ProcessRunner.cs
Comment thread src/SkillView.Core/Ui/SkillViewApp.cs Outdated
Comment thread src/SkillView.Core/Ui/LatestRequestGate.cs Outdated
Comment thread src/SkillView.Core/Ui/Tabs/InstalledTabView.cs Outdated
Comment thread src/SkillView.Core/Ui/Tabs/ChangesTabView.cs Outdated
Comment thread src/SkillView.Core/Ui/Tabs/UpdatesTabView.cs Outdated
Comment thread src/SkillView.Core/Logging/Logger.cs Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Shutdown deadlock, focus-routing, and overlapping update lifecycle issues remain unresolved.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (2)

Previously missed (2) — in code that hasn't changed since the last review.

src/SkillView.Core/Ui/SkillViewApp.cs:603

  • Esc does not actually leave every Discover input as advertised. The query field has its own Esc handler, but the owner/agent fields (and potentially the limit control) reach this branch; it only updates the status and consumes the key, so focus remains in the editor and the next q is still typed. Move focus to the results table before reporting that the field was left.
            SetStatus(TextInputHasFocus()
                ? "Esc leaves the field · Ctrl+Q quits"
                : "Press q or Ctrl+Q to quit");

tests/SkillView.Tests/Ui/SearchAgentMetadataCacheTests.cs:100

  • This test verifies capacity/FIFO eviction, but not least-recently-used behavior: no entry is accessed between insertion and eviction, so a FIFO cache would pass. Touch first before storing third, then assert that second was evicted.
  • Files reviewed: 41/41 changed files
  • Comments generated: 3
  • Review effort level: Balanced

Comment thread src/SkillView.Core/Logging/FileLogSink.cs Outdated
Comment thread src/SkillView.Core/Ui/SkillViewApp.cs Outdated
Comment thread src/SkillView.Core/Ui/Tabs/UpdatesTabView.cs

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Log initialization contains a race that can permanently omit concurrently emitted entries.

Review details

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

src/SkillView.Core/Ui/SkillViewApp.cs:1941

  • A log entry can be lost when logs are opened: an entry emitted after Logger.Snapshot() but before this lock is enqueued by OnLogEntry, then immediately removed by _visibleLogLines.Clear(). Take the logger snapshot while holding _visibleLogGate; callbacks for entries already added to the logger will then either be included in that snapshot or enqueue after initialization, preserving every wakeup.
        lock (_visibleLogGate)
        {
            _visibleLogLines.Clear();
            foreach (var line in lines) _visibleLogLines.Enqueue(line);
  • Files reviewed: 42/42 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

@harder
harder merged commit 40bbf0b into main Aug 28, 2026
9 checks passed
@harder
harder deleted the feat/tui-usability-hardening branch August 28, 2026 15:36
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.

2 participants