Skip to content

feat(feedback): port the feedback command to the refactor architecture - #2149

Open
jariy17 wants to merge 9 commits into
refactorfrom
feat/feedback-command
Open

feat(feedback): port the feedback command to the refactor architecture#2149
jariy17 wants to merge 9 commits into
refactorfrom
feat/feedback-command

Conversation

@jariy17

@jariy17 jariy17 commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

What

Implements agentcore feedback using a dedicated client and Handler.

Command surface

agentcore feedback <message> [--screenshot <path>] [--yes]
  • <message> — required (max 1000 chars).
  • --screenshot <path> — optional PNG/JPG, ≤100 MB.
  • --yes — accept the AWS Customer Agreement and skip the consent prompt.
  • --json — inherited global flag; envelope output.

Screenshot flow: presign POST → S3 PUT (SHA256 checksum + scanstatus=NOT_SCANNED tag) → form POST, referencing the object key parsed from the presigned URL (never fabricated).

Files

New: src/core/feedback.tsx (FeedbackClient + ApertureError extending AgentCoreCLIError with ERROR_SOURCE.SERVICE), src/handlers/feedback/{types,index,feedback.test}.tsx.
Wired: src/handlers/types.tsx (Core.feedback), src/core/index.tsx (CoreClient.feedback, injected fetch), src/handlers/index.tsx (root registration), src/testing/TestCoreClient.tsx (TestFeedbackClient).

Testing

  • bun test src/handlers/feedback — 7/7 pass (consent y/n, non-TTY guard, empty/oversized message, screenshot 3-call flow with checksum/tag headers + object-key assertion). tsc --noEmit clean, oxlint clean.
  • 3 real submissions to production Aperture verified end to end (text ×2, text+screenshot): ids f36eb155-…, 2e32ed65-…, 3525eaf4-…, all reference: agentcore-cli.

Adds `agentcore feedback <message> [--screenshot <path>] [--yes]`, which submits
to the Aperture public feedback API. Consent for the AWS Customer Agreement uses
the project/remove imperative pattern: a readline y/N prompt on a TTY, --yes to
accept non-interactively, and a hard failure (not a silent submit) when neither
a TTY nor --yes is present. Screenshot attachments go presign -> S3 PUT (SHA256
checksum + NOT_SCANNED tag) -> form POST, referencing the object key parsed from
the presigned URL.

- src/core/feedback.tsx: FeedbackClient (injected fetch) + ApertureError
  (ERROR_SOURCE.SERVICE) + payload/validation ported from the pre-refactor CLI
- src/handlers/feedback/: leaf handler with inline consent + types + flow tests
- wired onto Core, CoreClient, the root handler, and TestCoreClient
@github-actions github-actions Bot added the size/l PR size: L label Aug 31, 2026
@agentcore-devx-automation agentcore-devx-automation Bot added agentcore-harness-reviewing AgentCore Harness review in progress claude-security-reviewing Claude Code /security-review in progress labels Aug 31, 2026
@github-actions github-actions Bot added size/l PR size: L and removed size/l PR size: L labels Aug 31, 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 Aug 31, 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

The port is a clean, contained addition:

  • The consent flow mirrors src/handlers/project/remove's confirmRemoveAll/promptForRemoveAll verbatim, including the --yes/--json/TTY handling and the SIGINT+close cancellation pattern — good reuse of an established convention.
  • Tests exercise the real handler through CoreClient + createRootHandler, mocking only at the true I/O boundary (the injected fetch) and using a real temp dir for the screenshot fixture. No excessive mocking.
  • ApertureError extends AgentCoreCLIError with ERROR_SOURCE.SERVICE, so failures are classified rather than falling through as unknown.
  • Screenshot handling correctly parses the S3 object key from the presigned URL path, sends x-amz-checksum-sha256 + scanstatus=NOT_SCANNED, and validates extension/size/regular-file up front.
  • Telemetry: I checked src/handlers/** and src/router/** and the refactored handler architecture does not yet wire telemetry at the handler layer, so there is nothing to instrument here that other handlers are doing.

A couple of small things I noticed but that don't block merge:

  • FeedbackClient's clients: AwsClients constructor arg is unused — feedback lives outside the SDK seam. Could be dropped for clarity later.
  • submitForm casts response.json() directly to FeedbackSubmissionResult with no runtime validation; a partial response would silently be accepted.
  • loadScreenshot reads the whole file before checking MAX_SCREENSHOT_BYTES; you already stat the file, so gating on stats.size first would avoid loading a >100 MB file into memory only to reject it.

None of these require changes before merging.

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

codecov-commenter commented Aug 31, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.92929% with 21 lines in your changes missing coverage. Please review.
✅ Project coverage is 97.13%. Comparing base (f431dce) to head (c25a04c).
⚠️ Report is 16 commits behind head on refactor.

Files with missing lines Patch % Lines
src/core/feedback.tsx 91.18% 20 Missing ⚠️
src/handlers/feedback/index.tsx 98.48% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##           refactor    #2149      +/-   ##
============================================
- Coverage     97.16%   97.13%   -0.04%     
============================================
  Files           495      497       +2     
  Lines         32676    32973     +297     
============================================
+ Hits          31751    32027     +276     
- Misses          925      946      +21     

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

jariy17 added 5 commits August 31, 2026 19:56
The argument schema z.string().max(1000) double-validated the raw (untrimmed)
message and fired a generic zod error before core's friendlier, trim-aware
'must be 1000 characters or fewer' guard could run. Drop the arg constraint so
core.submitFeedback is the one code path that validates, matching the intent
noted in feedback/types.tsx.
The 100MB cap was checked on buffer.byteLength after readFile loaded the whole
file, so a multi-GB file was read entirely into memory just to be rejected.
Check stats.size before readFile instead.
…ient

FeedbackClient only uses the injected fetch (Aperture is outside the SDK seam),
so the stored AwsClients param was dead. Take only CoreFetch and update the
CoreClient construction site.
If Aperture returns a 2xx presign body that isn't a URL, new URL() threw a bare
TypeError that mapped to an internal-source error. Wrap it in ApertureError so
telemetry attributes the failure to the service.
--screenshot "" was falsy so it silently submitted with no attachment, unlike
every other bad screenshot value which errors. Reject a present-but-blank path
with an InputValidationError.
@github-actions github-actions Bot added size/l PR size: L and removed size/l PR size: L labels Aug 31, 2026
@agentcore-devx-automation agentcore-devx-automation Bot added the claude-security-reviewing Claude Code /security-review in progress label Aug 31, 2026
The rationale now lives on the FeedbackClient constructor in core/feedback.tsx.
@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 Aug 31, 2026
@github-actions github-actions Bot added size/l PR size: L and removed size/l PR size: L labels Aug 31, 2026
@agentcore-devx-automation agentcore-devx-automation Bot added the claude-security-reviewing Claude Code /security-review in progress label Aug 31, 2026
Follows the batch-evaluation pattern: golden-backed happy paths (text-only and
screenshot presign->S3 PUT->form, recorded against Aperture, replayed offline)
plus rejects.toThrow validation/consent cases (non-TTY without --yes, decline,
empty message, >1000 chars, empty --screenshot). Each submit test uses its own
fixtureFetch subdir since the fetch fixture key is method+path only and both
POST to /form. The presign response fixture has its X-Amz-* query stripped so no
signed URL is committed; replay keys on the stable object path.
@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 Aug 31, 2026
@github-actions github-actions Bot added size/l PR size: L and removed size/l PR size: L labels Aug 31, 2026
@agentcore-devx-automation agentcore-devx-automation Bot added the claude-security-reviewing Claude Code /security-review in progress label Aug 31, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

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

The feedback command was registered on the root handler in PR #2149 but
root.test.tsx's expected subcommand list was not updated, so 'builds the
agentcore command tree with its subcommands' failed in CI. Add 'feedback' in
its registration position (after eval).
@github-actions github-actions Bot added size/l PR size: L and removed size/l PR size: L labels Aug 31, 2026
@agentcore-devx-automation agentcore-devx-automation Bot added the claude-security-reviewing Claude Code /security-review in progress label Aug 31, 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 Aug 31, 2026
@jariy17
jariy17 marked this pull request as ready for review August 31, 2026 20:18

@aidandaly24 aidandaly24 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.

Left a few comments around compatibility and the request/fixture contracts.

Comment thread src/core/feedback.tsx
},
userAgent,
);
await this.uploadFileToS3(

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.

I think the new invalid-presign handling runs too late. uploadFileToS3() receives the raw body before objectKeyFromPresignedUrl() validates it; with a 200 not-a-url response I still got TypeError: fetch() URL is invalid, so the intended ApertureError never runs. Parsing the object key before the PUT should fix the classification and avoid uploading before the reference is known to be usable.

expect(result.reference).toBe("agentcore-cli");
}, 120_000);

test("submits feedback with a screenshot (presign → S3 PUT → form)", async () => {

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.

I do not think this golden test proves the request contract in its name. fixtureFetch keys only on method/path and ignores request headers and bodies; I replayed the PUT with no checksum/tagging headers and the form POST with {}, and both returned 200. I think we should keep the golden flow but restore a focused injected-fetch test for the checksum headers, scanstatus=NOT_SCANNED, and attachment object key.

// submission to the Aperture public API (and, for the screenshot case, uploads
// shot.png through a real presigned S3 PUT) — there is no undo, same as the
// batch-evaluation evaluate/simulate fixtures that submit real jobs. After a
// record run, strip the X-Amz-* query from the recorded presign Fetch fixture

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.

Can we avoid making presigned URL cleanup a manual recording step? fixtureFetch writes response.text() verbatim, so RECORD=1 puts the live X-Amz-* query on disk before this cleanup can happen. A recording sanitizer could write the queryless URL while still returning the original URL to the live upload flow.

@@ -0,0 +1,26 @@
import type { CoreOptions } from "../../core/types";

export interface ScreenshotInput {

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.

Can ScreenshotInput, SubmitFeedbackInput, and FeedbackSubmissionResult be type aliases? These are concrete request/result data shapes, while CoreFeedbackClient is the behavioral interface. The neighboring handler type modules use interfaces only for Core*Client contracts and types for determinate data; the internal records in core/feedback.tsx should probably follow the same pattern.

description: "Send feedback about the AgentCore CLI to the team.",
// Length/empty validation lives solely in core.submitFeedback so one code path
// guards every caller; the arg is unconstrained here beyond being a string.
arguments: [argument("message", "the feedback message to send", z.string())],

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 no-argument feedback wizard intentionally out of scope? Released v0.28.1 uses optional [message] and opens FeedbackScreen; this makes <message> required, and I confirmed bare agentcore feedback now exits 2. If this is staged, I think the compatibility gap should be tracked or called out; otherwise this should preserve the optional route.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/l PR size: L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants