Skip to content

fix(tui): esc from a deep-linked harness wizard returns to the menu - #2234

Merged
aidandaly24 merged 4 commits into
aws:refactorfrom
aidandaly24:fix/tui-escape-from-deep-linked-wizard
Sep 4, 2026
Merged

fix(tui): esc from a deep-linked harness wizard returns to the menu#2234
aidandaly24 merged 4 commits into
aws:refactorfrom
aidandaly24:fix/tui-escape-from-deep-linked-wizard

Conversation

@aidandaly24

@aidandaly24 aidandaly24 commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Description

Bug. agentcore harness create run with no flags opens the TUI directly on the create wizard, so the wizard is the only history entry. The wizard's first-step back was navigate(-1), which had nothing to pop, so esc did nothing. Same symptom as #2225 (the policy picker redirect loop), different mechanism. Reproduced against the compiled binary in a pty: agentcore harness createesc → screen unchanged.

Fix. HarnessWizard takes an explicit onExit, so each caller states where the first step's esc goes. harness create navigates to the harness menu, since it can be the first history entry. harness update keeps a history pop: it is only ever reached from the update picker or the detail screen's update action, and returns to whichever the user came from, as before. EndpointWizard keeps its pop, which is safe while every route into it passes a picker first; a comment records that.

Test. A route-table walk in src/components/Root.test.tsx: for every command with a screen (102 today), mount it through the real Root, press esc, and assert the header line belongs to a menu above it. This is the counterpart to the command-line-only walk in CliOnlyScreen.test.tsx, so a screen added later is covered without a new test. With this PR's three source files reverted to the branch point, the walk fails on exactly one case, agentcore harness create. The policy picker loop is already fixed on this base by #2225; before that landed, the walk failed on it too. The walk accepts any ancestor menu, so it catches a no-op or a loop but not an escape that jumps further up than it should. compiledRootCommand() is extracted from the test harness and the two inline copies in existing tests now use it.

Related Issue

No tracked issue. Found while bug-bashing the refactor branch TUI.

Documentation PR

None needed.

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update
  • Other (please describe):

Testing

How have you tested the change?

  • bun test (3099 pass, 0 fail); this branch has no separate test:unit / test:integ scripts
  • bun run typecheck
  • bun run lint:check
  • src/assets/ is untouched, so no snapshot update was needed
  • Verified in the compiled linux-x64 binary: agentcore harness createesc now lands on the harness menu

Checklist

  • I have read the CONTRIBUTING document
  • I have added any necessary tests that prove my fix is effective or my feature works
  • I have updated the documentation accordingly
  • I have added an appropriate example to the documentation to outline the feature, or no new docs are needed
  • My changes generate no new warnings
  • Any dependent changes have been merged and published

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the
terms of your choice.

`agentcore harness create` with no flags opens the TUI directly on the
wizard, so the wizard is the only history entry. Its first-step back was
navigate(-1), which had nothing to pop, and esc did nothing. The wizard
now takes an explicit onExit: create returns to the harness menu, update
returns to the update picker.
Walk the compiled command tree, mount every screen-backed command through
the real route table, press esc, and assert the header belongs to a menu
above it. This is the counterpart to the command-line-only walk in
CliOnlyScreen.test, so a screen added later is covered without a new
test. It catches both the policy picker redirect loop (aws#2225) and the
deep-linked wizard. compiledRootCommand() is extracted from the harness
so tests can enumerate the tree the same way the app does.
@github-actions github-actions Bot added the size/m PR size: M label Sep 4, 2026
@agentcore-devx-automation agentcore-devx-automation Bot added claude-security-reviewing Claude Code /security-review in progress agentcore-harness-reviewing AgentCore Harness review in progress labels Sep 4, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

Claude Security Review: no high-confidence findings. (run)

@agentcore-devx-automation agentcore-devx-automation Bot removed the claude-security-reviewing Claude Code /security-review in progress label Sep 4, 2026

@agentcore-devx-automation agentcore-devx-automation Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

AgentCore Harness Review

Verdict: Looks good

Nice fix. Replacing navigate(-1) with an explicit onExit target correctly handles the case where agentcore harness create / agentcore harness update <id> seed the memory history with a single entry (making the previous navigate(-1) a no-op). Moving the navigation decision to the parent screen keeps HarnessWizard route-agnostic, and the new tree-walking Root.test.tsx guards against this class of regression going forward for any command with a screen.

A couple of things I considered but don't think need to change:

  • For HarnessUpdateScreen, onExit now always goes to the picker (/agentcore/harness/update), even when the user arrived from /agentcore/harness/get/:id via the "update" action — where a working navigate(-1) would have popped back to the get screen. This is a consistency trade-off (same destination regardless of entry point) and it matches the create screen's behavior, so it seems intentional.
  • CliOnlyScreen.test.tsx still has its own local compiledRoot() helper that duplicates the newly-exported compiledRootCommand. Could be deduplicated in a follow-up but not required here.

Nit (feel free to ignore): the .catch(() => {}) on waitFor in Root.test.tsx is a nice touch — the subsequent expect(menus).toContain(...) produces a much better failure message than a bare timeout — a one-line comment saying so would help the next reader.

@agentcore-devx-automation agentcore-devx-automation Bot removed the agentcore-harness-reviewing AgentCore Harness review in progress label Sep 4, 2026
@codecov-commenter

codecov-commenter commented Sep 4, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.00000% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 97.00%. Comparing base (0d3fdc8) to head (46bccc2).
⚠️ Report is 5 commits behind head on refactor.

Files with missing lines Patch % Lines
src/components/EndpointWizard.tsx 0.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##           refactor    #2234      +/-   ##
============================================
+ Coverage     96.97%   97.00%   +0.03%     
============================================
  Files           559      559              
  Lines         38612    38624      +12     
============================================
+ Hits          37445    37469      +24     
+ Misses         1167     1155      -12     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

AlexanderRichey
AlexanderRichey previously approved these changes Sep 4, 2026
The update wizard is only reached from the update picker or the detail
screen's update action, never as the first history entry, so a pop returns
to whichever the user came from. Only the create wizard can be deep-linked
and needs an explicit target. Note the same invariant at EndpointWizard's
pop, which the route walk cannot reach because every route into it passes
a picker first.
Drop the two inline root compiles now that the harness exports one, and
record that the walk accepts any ancestor menu, so it catches a no-op or a
loop but not an escape that jumps too far up.
@github-actions github-actions Bot added size/m PR size: M and removed size/m PR size: M labels Sep 4, 2026
@agentcore-devx-automation agentcore-devx-automation Bot added the claude-security-reviewing Claude Code /security-review in progress label Sep 4, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

Claude Security Review: no high-confidence findings. (run)

@agentcore-devx-automation agentcore-devx-automation Bot removed the claude-security-reviewing Claude Code /security-review in progress label Sep 4, 2026
@aidandaly24
aidandaly24 merged commit 6731091 into aws:refactor Sep 4, 2026
18 of 21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m PR size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants