feat(aws-control): remove an account from the accounts pane - #8943
Conversation
Design Review (Fable 5) — ✅ PASSDesign-level review of Established Design-Verdict: PASS A real reported gap closed with the exact inverse of the existing register path; registry-only scope, consent-sweep ordering, and failure sides are all deliberate and documented. [DESIGN-REVIEWED] 0451650 |
First Principles Review (Fable 5) — 🟡 CONCERNSPremise-level review of All mechanical checks are done: First-Principles-Verdict: CONCERNS
What this change shipsIntent: let an operator remove a mistakenly-added AWS account from the page that added it — an ADDITION, anchored to a quoted user report.
Watch
Subtractions
[FIRST-PRINCIPLES-REVIEWED] 0451650 |
Opus 4.8 Review — ✅ no blocking findingsReviewed Review detailsI've analyzed both candidates against the diff and the code the diff contains. Candidate 1 (partial consent withdrawal across a multi-key account): The scenario requires a transient Candidate 2 (committed No grounded Step 2 findings: the feature validates names against No findings. [OPUS-REVIEWED] 0451650 Verdict parsed from the review's SHA-scoped output markers for commit False positive or not applicable? A repository writer can comment: |
GPT 5.6 Review — ✅ human override acceptedHuman judgment by @bolichen97 overrides the GPT 5.6 finding for This comment is updated in place on each push. The model was not re-run because an authorized human decision supersedes it. False positive or not applicable? A repository writer can comment: |
UX Review (Fable 5) — 🟡 CONCERNSUX-level review of Reconciliation is complete. Every control this PR adds appears in the screenshots, both themes and widths are covered, the blind reader correctly identified the overflow menu, the remove item, both confirm strips, the error state and the success state, and dared to use the primary path. Two genuine risks survive: the open dropdown visually detaches from its row (the reader couldn't tell which account it would remove), and the unresolved pseudo-row groups all orphan keys into one row, so its singular "Forget key" confirm can silently cover several keys. UX-Verdict: CONCERNS The remove flow reads well end-to-end, but the open row menu doesn't say which row it belongs to, and "Forget key" can silently mean several keys. Watch
Suggestions
[UX-REVIEWED] 0451650 |
625c69c to
319ec7c
Compare
|
Pushed GPT 5.6 (both fenced findings fixed)
UX Review
First Principles — Frontend Tests (3) — the shard fails on |
319ec7c to
c81e56e
Compare
c81e56e to
3554a23
Compare
|
Pushed GPT 5.6 F1 (fixed) — UX Review (both Watch items)
|
|
GPT 5.6 round 3 — not fixing this one; reasoning below. The finding is the third residual of the same class: an operator confirming consent for profile A concurrently with removing profile A, then re-registering A. The adjudicator's own record (FLAG, "likely edge case") already states why the harm is bounded:
Closing it "properly" means the consent confirm handler (
Design Review's suggestion (surface the re-picked default) is noted but deferred: First Principles already flags unread response fields, and the default is visible on the Keys card via the star. |
Registration was one-way: a profile added to AWS Control had no control that took it back out, and a user who registered a key by mistake kept it on the page for good. Every account row now carries an overflow menu whose one item reveals an inline confirm strip, and confirming posts the row's profile names to a new POST /profiles/unregister. Removal is registry-only by construction: the route never reaches the module's one `aws configure` writer or the AWS CLI, so the operator's AWS CLI configuration and every AWS resource the account holds, the drive bucket included, are untouched. Names are checked against the shared profile pattern but not against the machine's profile list, so a stale entry whose profile was already deleted is removable. Grants are keyed by service, so `aws_consent.revoke_for_profile` sweeps the gated services and withdraws every grant naming a removed profile; the share, library, and backup ledgers stay because they describe the bucket, not the key. The registry default is re-picked when the removed profile held it. The menu trigger sits beside the row's select button rather than inside it, so opening it cannot select (and jump to) the account about to be removed. The confirm strip reuses the Files/Library strip and stays open until the request resolves, since it is the only place the outcome can render.
3554a23 to
0451650
Compare
|
/ai-review override gpt 0451650: same-account edge race, harm bounded by the uncached live-account re-probe on every paid call; residual grant is visible and withdrawable |
Human judgment recorded@bolichen97 marked the gpt AI finding as false positive, not applicable, or explicitly accepted for
This decision applies only to this commit. A new push requires a new judgment. |
Summary
A user on Discord asked: "once you add an AWS account to AWS Control… you can't remove it via the UX??" — and they were right: registration was one-way. This adds the removal path.
POST /api/apps/aws-control/profiles/unregister({names: [...]}), mirroring/profiles/register. Registry-only by construction — the handler never reachesdeploy.profiles.create_aws_profile(the oneaws configurewriter) or the AWS CLI, so the operator's AWS CLI configuration and every AWS resource (drive bucket included) are untouched. Names are validated against the shared profile pattern but not against the machine's profile list, so a stale entry whose profile was already deleted locally is removable. The registry default is re-picked when the removed profile held it.aws_consent.revoke_for_profilesweeps the gated services and withdraws every grant naming a removed profile — a later re-registration under the same name starts unconsented. Share / library / backup ledgers are deliberately left alone: they describe the bucket, which still exists and still bills.⋮overflow menu beside the select button (not inside it, so opening the menu cannot select and jump to the account about to be removed). The one item reveals the same inline Cancel + danger strip the Files/Library folders use; it names the account (or, for the unresolved pseudo-row, the key it will forget) and states that nothing in AWS or the AWS CLI configuration changes. The strip stays open until the request resolves; a refusal renders there with the agent hand-off. Success refetches accounts, Add-accounts, and the consent receipts.apps.awsControl.pagein all 12 catalogs;remove_account_confirmregistered inQUOTED_OPERAND_CONFIRM_KEYS.docs/system-specs/features/aws-control.md(consent section + HTTP surface).Screenshots
Row menu (dark, 1280):
Confirm strip:
Refused removal, narrow (light, 390):
More frames
Tests
TestProfileUnregister(10 cases: body/pattern 400s, all-unknown 404, removal + default re-pick + skipped names, last-profile empties default, grant sweep is profile-scoped, structural pin that no credential writer or CLI is reached, SELprofiles_unregistersuccess audit);("POST", "/profiles/unregister")added toP0_ROUTESso the disabled / non-owner / restricted-session gates cover it;revoke_for_profileunit test intest_aws_consent.py.AwsControlPage.test.tsx(menu does not select, confirm posts every key and refetches, cancel sends nothing, refusal stays on the strip, unresolved row names its key).Verification
tsc, eslint, jscpd,
i18n:check, isort / flake8 / mypy / black — clean. Full backend suite: 89 217 passed; the 219 failures are the host's pre-existing set (218 reproduce on a cleanorigin/maincheckout of the same files; the one extra,test_api_health::test_a_forgetful_pre_audit_refusal_is_still_audited_by_position, also fails alone on clean main). Full frontend suite: 29 473 passed;PromptsTab.test.tsxfailed once in the full run and passes alone on both trees (ordering flake, untouched by this diff).