') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); feat(eval): read-only ab-test commands + TUI by jariy17 · Pull Request #2102 · aws/agentcore-cli · GitHub
Skip to content

feat(eval): read-only ab-test commands + TUI - #2102

Merged
jariy17 merged 5 commits into
refactorfrom
feat/eval-ab-test-readonly
Aug 26, 2026
Merged

feat(eval): read-only ab-test commands + TUI#2102
jariy17 merged 5 commits into
refactorfrom
feat/eval-ab-test-readonly

Conversation

@jariy17

@jariy17jariy17 commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Adds the full read-only + write agentcore eval ab-test command family, plus the interactive TUI. Five commits.

Command + TUI structure

agentcore eval ab-test TUI menu shows get + list only
├── get --id read · JSON to stdout
├── list --next-token --max-results read · JSON page to stdout
├── pause --id write · executionStatus → PAUSED
├── resume --id write · executionStatus → RUNNING
├── stop --id write · executionStatus → STOPPED
└── delete --id write · DeleteABTestCommand

How it works

A/B tests are imperative jobs (like batch-evaluation), not project resources. All commands are one-liners through the data-plane SDK client @aws-sdk/client-bedrock-agentcore:

  • get/listGetABTestCommand / ListABTestsCommand. get returns per-evaluator metrics inline (no CloudWatch).
  • pause/resume/stopUpdateABTestCommand({ abTestId, executionStatus }) — the same shape online-eval pause/resume uses.
  • deleteDeleteABTestCommand.
  • TUI: menu → picker → detail hub + JSON, modeled on runtime. Write commands are CLI-only and kept out of the interactive menu via .supportedTuiCommands("get","list") (matches online-eval).

Known / deferred

  1. stop gateway-rule suggestion — after stopping, the doc calls for printing a suggested (never executed) update-gateway-rule to promote the winner. Left as a // TODO this PR; tracked as a follow-up.
  2. --json error path — failures print plain-text Error: … to stderr, not a JSON error object. Shared-renderer issue, not ab-test-specific; follow-up.
  3. Transition validity (pause-on-RUNNING, delete-on-RUNNING, etc.) is enforced server-side; the CLI forwards and surfaces the service error. No client-side state machine, matching online-eval.

Files

AreaFiles
CLI handlershandlers/eval/ab-test/{get,list,pause,resume,stop,delete}/index.tsx, index.tsx
TUI screenshandlers/eval/ab-test/{screen,list/screen,get/screen}.tsx, components/AbTestPicker.tsx, 5 routes in Root.tsx
Corecore/eval.tsx (getABTest/listABTests/setABTestExecutionStatus/deleteABTest), handlers/eval/types.tsx, testing/TestCoreClient.tsx

Tests

  • Golden fixture (ab-test.fixture.test.tsx, like harness): real router → CoreClient → recorded SDK → matchGolden. get / list / not-found.
  • Screen (ab-test.screen.test.tsx, like runtime): menu (read-only), picker, hub, json, retry.
  • Command-flow (ab-test.write.test.tsx, like batch-evaluation): hierarchy = get/list/pause/resume/stop/delete; each transition's executionStatus; delete; required --id.

Bug bash — all green

  • bun test src/handlers/eval src/components → 338+ pass / 0 fail; bun run typecheck → 0 errors; oxlint → clean.
  • 4-agent /review-pr + a live non-destructive bug-bash on the exploratory account (us-west-2): all 6 commands dispatch correctly; write verbs return a clean not-found on a bogus id (no mutation); --id guard fires pre-network. One real bug found & fixed in this PR: the TUI menu listed write commands (dead-end) — now .supportedTuiCommands.

Follow-ups (separate PRs)

  • stop's gateway-rule promotion suggestion
  • structured --json error output (shared renderer)
  • remove stale projectSchemas/ab-test.ts (contradicts the job model)

@github-actionsgithub-actionsBot added the size/m PR size: M label Aug 25, 2026
@agentcore-devx-automationagentcore-devx-automationBot added agentcore-harness-reviewing AgentCore Harness review in progress claude-security-reviewing Claude Code /security-review in progress and removed agentcore-harness-reviewing AgentCore Harness review in progress labels Aug 25, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

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

@agentcore-devx-automationagentcore-devx-automationBot removed the claude-security-reviewing Claude Code /security-review in progress label Aug 25, 2026
@codecov-commenter

codecov-commenter commented Aug 25, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.61279% with 16 lines in your changes missing coverage. Please review.
✅ Project coverage is 97.30%. Comparing base (343d213) to head (ba2e9cc).
⚠️ Report is 5 commits behind head on refactor.

Files with missing linesPatch %Lines
src/core/eval.tsx38.46%16 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## refactor #2102 +/- ##
============================================
- Coverage 97.33% 97.30% -0.04% 
============================================
Files 417 428 +11 Lines 25250 25546 +296 ============================================
+ Hits 24578 24858 +280 - Misses 672 688 +16 

☔ 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.

Add `agentcore eval ab-test get` and `list`, modeled on batch-evaluation.
A/B tests are imperative jobs on the data-plane client, so this wires the
existing @aws-sdk/client-bedrock-agentcore GetABTestCommand / ListABTestsCommand
through EvalClient — no SigV4, no new module.
- get: --id only; GetABTest returns per-evaluator metrics inline (no CloudWatch)
- list: --next-token / --max-results passthrough (service supports no filters)
- TestCoreClient: getABTest/listABTests mocks + setters
- command-flow tests
TUI screens and mutating commands deferred to later phases.
@jariy17
jariy17force-pushed the feat/eval-ab-test-readonly branch from 6228f89 to 520f0beCompareAugust 25, 2026 20:04
@github-actionsgithub-actionsBot added size/m PR size: M and removed size/m PR size: M labels Aug 25, 2026
@agentcore-devx-automationagentcore-devx-automationBot added the claude-security-reviewing Claude Code /security-review in progress label Aug 25, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

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

@agentcore-devx-automationagentcore-devx-automationBot removed the claude-security-reviewing Claude Code /security-review in progress label Aug 25, 2026
@jariy17
jariy17 marked this pull request as ready for review August 25, 2026 20:06
Interactive screens for `agentcore eval ab-test`, modeled on the runtime TUI:
- AbTestScreen menu (RouterScreen)
- AbTestListScreen → AbTestPicker (wraps PaginatedTablePicker, listABTests)
- AbTestGetScreen detail hub (ResourceDetailScreen) + AbTestGetJsonScreen
- 5 routes wired in Root.tsx
Screen tests via renderScreen mirror runtime.screen.test.tsx. No CLI/core
changes; reuses the getABTest/listABTests methods + mocks from the read-only
commit.
@github-actionsgithub-actionsBot added size/l PR size: L and removed size/m PR size: M labels Aug 25, 2026
@jariy17jariy17 changed the title feat(eval): read-only ab-test get/list commandsfeat(eval): read-only ab-test commands + TUIAug 25, 2026
@agentcore-devx-automationagentcore-devx-automationBot added the claude-security-reviewing Claude Code /security-review in progress label Aug 25, 2026
@github-actionsgithub-actionsBot added size/l PR size: L and removed size/l PR size: L labels Aug 25, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

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

@agentcore-devx-automationagentcore-devx-automationBot removed the claude-security-reviewing Claude Code /security-review in progress label Aug 25, 2026
notgitika
notgitika previously approved these changes Aug 25, 2026
Comment on lines +29 to +30
status: summary.status ?? "-",
executionStatus: summary.executionStatus ?? "-",

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.

discussed offline and we see that this is the output from the API. i would imagine as customer would be confused seeing these 2 fields in the data table in TUI. we can try to unify it in the future maybe

],
},
...overrides,
} as GetABTestResponse;

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.

is the reason we are not using satisfies that we have treatmentname and not variantname? I see that in the fixture

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Good catch — switched to satisfies GetABTestResponse in c2e0c18. It immediately surfaced that the stub had treatmentName (the real metric field is variantName, matching the recorded fixture) and was missing Variant.variantConfiguration. Both fixed; the cast was hiding them.

Comment threadsrc/core/eval.tsx
Comment on lines +397 to +398
nextToken: string | undefined,
maxResults: number | undefined,

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.

nice that we have these as just passthroughs :)

Addresses review: swap the `as GetABTestResponse` cast for `satisfies`,
which surfaced two wrong fields in the stub — the metric field is
variantName (not treatmentName) and Variant requires variantConfiguration.
Fill the required response fields so the stub type-checks.
@github-actionsgithub-actionsBot added size/l PR size: L and removed size/l PR size: L labels Aug 25, 2026
@agentcore-devx-automationagentcore-devx-automationBot added the claude-security-reviewing Claude Code /security-review in progress label Aug 25, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

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

@agentcore-devx-automationagentcore-devx-automationBot removed the claude-security-reviewing Claude Code /security-review in progress label Aug 25, 2026
notgitika
notgitika previously approved these changes Aug 25, 2026
pause/resume/stop set executionStatus via UpdateABTestCommand (same shape as
online-eval pause/resume, on the data-plane client); delete uses
DeleteABTestCommand. All four are --id-only leaf handlers registered on the
ab-test router. stop carries a TODO for the (never-executed) gateway-rule
promotion suggestion. Command-flow tests cover the hierarchy, each transition's
executionStatus, delete, and the required --id.
@github-actionsgithub-actionsBot added size/xl PR size: XL and removed size/l PR size: L labels Aug 25, 2026
@agentcore-devx-automationagentcore-devx-automationBot added the claude-security-reviewing Claude Code /security-review in progress label Aug 25, 2026
@jariy17
jariy17 marked this pull request as draft August 25, 2026 23:24
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

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

@agentcore-devx-automationagentcore-devx-automationBot removed the claude-security-reviewing Claude Code /security-review in progress label Aug 25, 2026
Add .supportedTuiCommands("get", "list") to the ab-test router, matching
online-eval. Without it the interactive menu listed pause/resume/stop/delete,
which have no screen routes and dead-ended on the help screen. Add a menu
screen test that fails without the fix.
@github-actionsgithub-actionsBot added size/xl PR size: XL and removed size/xl PR size: XL labels Aug 25, 2026
@agentcore-devx-automationagentcore-devx-automationBot added the claude-security-reviewing Claude Code /security-review in progress label Aug 25, 2026
@github-actionsgithub-actionsBot added size/xl PR size: XL and removed size/xl PR size: XL labels Aug 25, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

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

@agentcore-devx-automationagentcore-devx-automationBot removed the claude-security-reviewing Claude Code /security-review in progress label Aug 25, 2026
@jariy17
jariy17 marked this pull request as ready for review August 26, 2026 14:21
@jariy17
jariy17 merged commit f924f12 into refactorAug 26, 2026
29 checks passed
@jariy17
jariy17 deleted the feat/eval-ab-test-readonly branch August 26, 2026 14:21
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/xlPR size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@jariy17@codecov-commenter@AlexanderRichey@notgitika