Skip to content

fix(purchases): record partial success and stamp target account on history (closes #642, #646) - #650

Merged
cristim merged 2 commits into
feat/multicloud-web-frontendfrom
fix/issues-642-646-partial-failure
May 22, 2026
Merged

fix(purchases): record partial success and stamp target account on history (closes #642, #646)#650
cristim merged 2 commits into
feat/multicloud-web-frontendfrom
fix/issues-642-646-partial-failure

Conversation

@cristim

@cristim cristim commented May 21, 2026

Copy link
Copy Markdown
Member

Summary

Fixes two execution-core correctness issues (#642 partial-failure handling, #646 wrong account stamped on history).

#642 — partial success no longer reads as total failure

New partially_completed status, used whenever at least one rec committed a real purchase and at least one failed. Such a row is NEVER marked failed (the double-spend hazard the issue warns about), and the success notification is sent for the recs that purchased.

  • Single-account: extracted executeSingleAccount returning a *partialPurchaseError; finalizeExecution maps it via errors.As to partially_completed (preserving any audit-gap note via a shared appendErrNote).
  • Multi-account fan-out (executeForAccount): sets partially_completed inline + notifies, still surfaces per-rec failures to the aggregator.
  • History: partially_completed added to historyExecutionStatuses, always synthesised, flagged IsAuditGap (excludes execution-level dollars; committed dollars come from the per-rec purchase_history rows), clear StatusDescription. Frontend: partial fan-out toast names the still-actionable submitted buckets + a "Partial" history badge.

#646 — correct account on history

resolveSingleAccountProvider now also returns the resolved target account's ExternalID; the single-account path stamps that (matching the fan-out path), falling back to ambient AWS STS only when no target account is identified. Fixes assume-role AWS and direct-execute Azure/GCP.

Test plan

  • Go: single + multi-account partial -> partially_completed + notification; AWS + Azure correct account stamping; History synthesis + dollar exclusion
  • TS: fan-out partial toast names submitted buckets
  • Go purchase+api 1306 pass (1 pre-existing unrelated failure, see below); frontend 1915 pass; gofmt/gocyclo clean

Closes #642, #646.

Note: TestManager_RecoverStrandedApprovals_LateCompletionNotClobbered fails on the unmodified base branch (confirmed via git stash) — unrelated to this change, tracked separately.

Summary by CodeRabbit

Bug Fixes

  • Improved visibility and messaging for partial purchase failures across the app.
  • Added a “Partial” warning badge to distinguish partially completed executions.
  • Enhanced toast notifications to list succeeded buckets (with pending approval) and indicate "X of Y" failures.
  • Ensured purchase history records correctly reflect target account identity for executed purchases.

Review Change Stack

…story

Partial-failure runs (some recs commit, others fail) were marked the whole
execution "failed" and skipped the success notification despite real
commitments written to purchase_history with Purchased=true, inviting a
re-approve that double-buys the already-purchased recs (#642). The
single-account path also stamped the ambient AWS host account on history
regardless of the resolved target account or provider (#646).

- #642: introduce a "partially_completed" outcome. When at least one rec
  committed, never mark the row "failed"; record partially_completed, send
  the confirmation for the recs that purchased, and surface the row in
  History (flagged IsAuditGap so its execution-level dollars are excluded -
  the committed dollars come from the per-rec purchase_history rows). Applies
  to both the single-account path (via a partialPurchaseError sentinel that
  finalizeExecution maps) and the multi-account fan-out path.
- #642 frontend: on a partial fan-out failure, name the buckets that created
  pending executions so the still-actionable requests are not silent orphans;
  add a distinct "Partial" history badge.
- #646: resolve the target account's ExternalID from the resolved account and
  stamp that on purchase_history, falling back to the ambient AWS STS identity
  only when no target account can be identified. Fixes assume-role AWS and
  direct-execute Azure/GCP stamping.

Regression tests: single-account + multi-account partial success
(partially_completed status + notification sent), correct account stamping
for AWS and Azure, History row synthesis with dollar-exclusion, and the
fan-out partial toast naming submitted buckets.

Closes #642
Closes #646
@cristim cristim added bug Something isn't working triaged Item has been triaged priority/p2 Backlog-worthy severity/medium Moderate harm urgency/this-quarter Within the quarter impact/many Affects most users effort/m Days type/bug Defect labels May 21, 2026
@coderabbitai

coderabbitai Bot commented May 21, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 173b5712-cb14-4872-be9a-a7b908f1393a

📥 Commits

Reviewing files that changed from the base of the PR and between 70bd130 and f9eaf91.

📒 Files selected for processing (2)
  • internal/purchase/execution.go
  • internal/purchase/execution_test.go

📝 Walkthrough

Walkthrough

This PR implements consistent handling of partial failures across purchase execution flows. When some recommendations succeed and others fail, the backend now records a new partially_completed status, sends confirmations for committed recommendations, and stamps the correct target account in history. The frontend fan-out flow displays succeeded pending buckets in the warning toast for transparency.

Changes

Partial Purchase Execution Handling

Layer / File(s) Summary
Core partial-failure execution logic
internal/purchase/execution.go, internal/purchase/manager.go
executeSingleAccount and executeForAccount now detect partial success via anyRecPurchased gate and distinguish it from total failure. partialPurchaseError sentinel allows finalizeExecution to record partially_completed status, append error notes without overwriting, and set CompletedAt. Confirmations are sent for committed recs even in partial-success cases. resolveSingleAccountProvider now returns target ExternalID for correct history stamping.
History audit integration
internal/api/handler_history.go, frontend/src/history.ts
Partially-completed executions are registered in history status list and annotated as audit gaps (IsAuditGap=true) with approver metadata and descriptive failure details. Frontend renders a "Partial" warning badge distinct from completed/failed/cancelled states.
Backend partial-success tests
internal/api/handler_history_test.go, internal/purchase/execution_test.go
New regression tests verify single-account and per-account partial success (one rec succeeds, one fails; status marked partially_completed, confirmations sent, history includes only purchased recs) and correct target-account ExternalID stamping for AWS and Azure single-account purchases.
Frontend fan-out partial-failure toast
frontend/src/app.ts, frontend/src/__tests__/purchase-execution-toast.test.ts
handleFanOutExecute computes and appends a submittedNote listing up to five succeeded pending buckets to the warning toast when partial failure occurs. Test helper extends bucket objects with provider and service fields and verifies partial-failure toast surfaces submitted buckets while excluding failed ones.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • LeanerCloud/CUDly#603: Modifies resolveSingleAccountProvider credential resolution logic that this PR extends with target ExternalID return.
  • LeanerCloud/CUDly#628: Updates error-note composition in recordHistoryAuditGap that this PR builds upon with appendErrNote helper.
  • LeanerCloud/CUDly#623: Introduces IsAuditGap audit-gap marking in history that this PR extends to treat partially_completed status as an audit gap.

Poem

A rabbit once split up a purchase in two,
One half went through, but the other just flew.
Now partial's recorded, not failed, but aware—
Confirmations for winners, and audit's fair! 🐰✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main changes: introducing partial success handling and target account stamping on purchase history, directly addressing both linked issues (#642 and #646).
Linked Issues check ✅ Passed The PR fully implements requirements from #642: introduces partially_completed status preventing failed marking despite commitments, sends success notifications for purchased recs, surfaces succeeded buckets in fan-out toast, and adds comprehensive regression tests for backend and frontend partial-failure scenarios.
Out of Scope Changes check ✅ Passed All changes are directly scoped to #642 and #646: partial-failure handling (status, notifications, history synthesis, frontend UI), target account stamping and credential resolution, and related test coverage. No unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 88.89% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/issues-642-646-partial-failure

Comment @coderabbitai help to get the list of available commands and usage tips.

@cristim

cristim commented May 21, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 21, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/purchase/execution.go`:
- Around line 260-262: When GetCloudAccount returns nil while a specific
cloudAccountID has been resolved, do not silently return (nil, "", nil) which
allows fallback to ambient credentials; instead detect that cloudAccountID (or
the variable holding the target account ID) is set and return a descriptive
error indicating the target account could not be resolved. Update the nil-check
block around GetCloudAccount/account (the section containing "if account == nil
{ return nil, \"\", nil }") to return an error that includes the cloudAccountID
and mentions unresolved account, so callers cannot continue and accidentally use
ambient credentials.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 85308fdf-8137-4422-997b-61ad77428f1c

📥 Commits

Reviewing files that changed from the base of the PR and between 45a92e7 and 70bd130.

📒 Files selected for processing (8)
  • frontend/src/__tests__/purchase-execution-toast.test.ts
  • frontend/src/app.ts
  • frontend/src/history.ts
  • internal/api/handler_history.go
  • internal/api/handler_history_test.go
  • internal/purchase/execution.go
  • internal/purchase/execution_test.go
  • internal/purchase/manager.go

Comment thread internal/purchase/execution.go
…ack to ambient

When a single-account purchase resolves a concrete cloudAccountID but
GetCloudAccount returns a nil account (the account does not exist),
resolveSingleAccountProvider returned (nil, "", nil). The caller then
treated the empty target ID as "truly ambient" and fell back to the host
AWS STS identity, purchasing and stamping purchase_history against the
wrong account (#646).

Surface a descriptive error instead so the caller's no-ambient-fallback
contract holds once a target account is known. Add a regression test
covering the not-found path.
@cristim

cristim commented May 22, 2026

Copy link
Copy Markdown
Member Author

Addressed the actionable finding: resolveSingleAccountProvider now returns a descriptive error when a target cloudAccountID is known but GetCloudAccount returns a nil account, instead of (nil, "", nil) which let the caller fall back to ambient AWS credentials and stamp purchase_history against the wrong account (#646). Added regression test TestExecutePurchase_SingleAccount_AccountNotFound and updated the function contract docstring. Financial-correctness invariants are preserved (no row with real commitments is marked failed; partially_completed dollars still come from per-rec purchase_history rows; the change strengthens correct-target-account stamping). Commit f9eaf91.

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 22, 2026

Copy link
Copy Markdown
Contributor

@cristim Thanks for the detailed description of the fix. The change to resolveSingleAccountProvider — returning a descriptive error instead of the silent (nil, "", nil) fallback — is a solid correctness improvement. The previous behavior could silently stamp purchase_history against the wrong (ambient AWS) account whenever a target account was expected but not resolvable, so surfacing that as an explicit error is the right call. The regression test TestExecutePurchase_SingleAccount_AccountNotFound closes that coverage gap nicely.

Triggering a fresh review now.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim

cristim commented May 22, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 22, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

Labels

bug Something isn't working effort/m Days impact/many Affects most users priority/p2 Backlog-worthy severity/medium Moderate harm triaged Item has been triaged type/bug Defect urgency/this-quarter Within the quarter

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant