Skip to content

fix: defer policy engine write and harden policy flow UX#856

Merged
jesseturner21 merged 3 commits intomainfrom
fix/policy-engine-escape-goes-back
Apr 17, 2026
Merged

fix: defer policy engine write and harden policy flow UX#856
jesseturner21 merged 3 commits intomainfrom
fix/policy-engine-escape-goes-back

Conversation

@jesseturner21
Copy link
Copy Markdown
Contributor

@jesseturner21 jesseturner21 commented Apr 15, 2026

Problem

Two issues in the policy engine TUI flow:

  1. Escape skipped to success instead of going back — When adding a policy engine through the interactive TUI (agentcore → add → policy engine), pressing Escape on the "Attach to gateways" screen would skip forward to the success screen instead of going back. The engine was written to agentcore.json immediately at the name step, so the user couldn't back out without leaving a half-configured engine on disk.

  2. Policy generation failures crashed the TUI — Unhandled rejections from the generate() call hit the global process.exit(1) handler in index.ts, causing the TUI to exit immediately. The error screen also had no active useInput handler, so Escape did nothing despite the hint text.

Fix

Commit 1: Defer engine write until flow completes

  • Defer the disk writepolicyEnginePrimitive.add() is no longer called at the name step. A new commitEngine helper writes the engine (and optionally attaches gateways) only when the user reaches the final confirmation.
  • Escape goes back — on the gateway selection screen, Escape now returns to the name step instead of skipping to success. Since nothing is persisted yet, going back is clean.
  • Three terminal write paths — the engine is written to disk only when:
    1. No unprotected gateways exist (flow completes at name step)
    2. User presses Enter with no gateways selected
    3. User selects gateways and picks an enforcement mode

Commit 2: Preserve engine name on back navigation

  • When navigating back from the gateway screen, the previously entered engine name is restored via pendingEngineName state, instead of being replaced with a generated default.

Commit 3: Prevent TUI crash on policy generation failure

  • Add .catch() safety net on generate() to prevent unhandled rejections from reaching the global process.exit(1) handler
  • Add useInput handler for Escape on the error screen so users can go back and retry
  • Add ExecLogger to write structured logs to .cli/logs/policy-generation/
  • Show log file path on the error screen

Test plan

  • Escape on gateway screen → goes back to name step, agentcore.json unchanged
  • Enter with no gateways selected → engine written, success screen shown
  • Select gateways + pick mode → engine + gateway config written, success screen shown
  • Non-interactive CLI (agentcore add policy-engine --name X) still works
  • Back navigation preserves previously entered engine name
  • Policy generation failure shows error screen (no TUI crash)
  • Escape on generation error screen goes back to previous step
  • All 3304 unit tests pass

Previously, pressing Escape on the gateway selection screen during
policy engine creation would skip to the success screen because the
engine was already written to agentcore.json at the name step. Now
the disk write is deferred until the user completes the entire flow,
so Escape correctly navigates back to the previous step without
persisting a half-configured engine.

Constraint: Must not break non-interactive CLI path which still writes immediately via primitive
Rejected: Only change Escape to go back without deferring write | engine would still be persisted on back
Confidence: high
Scope-risk: narrow
@jesseturner21 jesseturner21 requested a review from a team April 15, 2026 14:09
@github-actions github-actions Bot added the size/s PR size: S label Apr 15, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Package Tarball

aws-agentcore-0.8.0.tgz

How to install

npm install https://github.com/aws/agentcore-cli/releases/download/pr-856-tarball/aws-agentcore-0.8.0.tgz

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 15, 2026

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 44.57% 7060 / 15840
🔵 Statements 44.04% 7491 / 17007
🔵 Functions 41.98% 1262 / 3006
🔵 Branches 43.59% 4752 / 10900
Generated in workflow #1805 for commit 8d22bdf by the Vitest Coverage Report Action

@jesseturner21 jesseturner21 changed the title fix: defer policy engine write until flow completes fix: defer policy engine write to disk until flow completes Apr 15, 2026
Copy link
Copy Markdown
Contributor Author

@jesseturner21 jesseturner21 left a comment

Choose a reason for hiding this comment

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

ISSUE BELOW FIXED

Used Option B


Issue: Escape → back to engine-wizard loses the user's previously entered name

Severity: Medium — UX regression

When the user presses Escape on the gateway selection screen, the flow transitions to { name: 'engine-wizard' }, which remounts AddPolicyEngineScreen. That component generates a fresh initialValue via generateUniqueName('MyPolicyEngine', existingEngineNames) — the name the user previously typed and submitted is discarded.

Scenario:

  1. User types engine name ProductionGuard → submits
  2. Gateway selection screen appears (unprotected gateways exist)
  3. User presses Escape to go back
  4. Engine name screen reappears with MyPolicyEngine (or similar generated name) instead of ProductionGuard

Options to address:

Option A — Carry the name in flow state: Add an optional engineName field to the engine-wizard flow state variant. When navigating back from attach-gateways, pass flow.engineName into it. Then thread that through to AddPolicyEngineScreen as an initialValue override. This is the cleanest fix — it's one new field on the state union and a prop on the child component.

// Flow state addition:
| { name: 'engine-wizard'; prefillName?: string }

// Back handler:
onBack={() => setFlow({ name: 'engine-wizard', prefillName: flow.engineName })}

// In AddPolicyEngineScreen rendering:
initialValue={flow.prefillName ?? generateUniqueName('MyPolicyEngine', existingEngineNames)}

Option B — Store the pending name in component state: Add a pendingEngineName state variable alongside the existing engineNames state. Set it when handleEngineComplete fires, clear it on success. Use it as the initial value when rendering engine-wizard. Slightly more state to manage but doesn't require changing the flow union type.

Option A is simpler and keeps the state local to the navigation path.

When pressing Escape on the gateway screen to go back to the name step,
the previously entered engine name was lost because AddPolicyEngineScreen
remounted with a generated default. Now the entered name is stored in
pendingEngineName state and passed back as initialName so the user sees
their original input.

Constraint: Must not change flow state union type to keep diff minimal
Rejected: Carry name in FlowState union variant | adds complexity to type for one field
Confidence: high
Scope-risk: narrow
@github-actions github-actions Bot added size/m PR size: M and removed size/s PR size: S labels Apr 15, 2026
This test requires a live terminal session and cannot run as a unit test
in CI. It was an untracked local file that got staged by mistake.
@jesseturner21 jesseturner21 changed the title fix: defer policy engine write to disk until flow completes fix: defer policy engine write and harden policy flow UX Apr 15, 2026
@github-actions github-actions Bot added size/s PR size: S and removed size/m PR size: M labels Apr 15, 2026
@jesseturner21 jesseturner21 merged commit c576d02 into main Apr 17, 2026
26 of 30 checks passed
@jesseturner21 jesseturner21 deleted the fix/policy-engine-escape-goes-back branch April 17, 2026 13:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/s PR size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants