Skip to content

fix(ri-exchange): answer the go exchange refusals with 422 instead of 500 - #1212

Merged
IAmJSD merged 3 commits into
LeanerCloud:mainfrom
IAmJSD:fix/issue-521
Oct 25, 2026
Merged

IAmJSD merged 3 commits into
LeanerCloud:mainfrom
IAmJSD:fix/issue-521

Conversation

@IAmJSD

@IAmJSD IAmJSD commented Oct 20, 2026

Copy link
Copy Markdown
Collaborator

Summary

#521 asked for a cloud-commitments-go bump plus refusal mapping. The bump is already on main, and so is the source-building item. The error mapping was still missing.

Issue item State on main before this PR This PR
Bump go to versions containing go#203 and go#209 Done by #1053 / #1138. pkg is at 541e05e5, providers/azure at 3def3f89. In cloud-commitments-go, git merge-base --is-ancestor puts both 81565ea1 (go#203) and 2d7f3f31 (go#209) inside every pinned version. No go.mod change
1. toAzureExchangeSources builds sources from the owned reservations, carries AppliedScopeType/AppliedScopes, and keeps the request Quantity Done by #475, pinned by TestAzureExchangeScopePreserved Unchanged
2. Map ErrQuoteNotUSD / ErrUnsupportedAppliedScope to a 4xx in mapAzureExchangeError and mapAWSExchangeError Missing. Both refusals came back as a generic 500. Fixed
3. Document applied_scope_type / applied_scopes in openapi.yaml Missing. The list response was a bare type: object. Added
4. Handler test that goes through the real request builder TestAzureExchangeScopeRefused ran the real CalculateExchange but only checked require.Error, so a 500 still passed Tightened, plus a new AWS test
5. Cap half of #361 Covered by go#203 (see below) Nothing more needed in the backend

Root cause

Before this PR, mapAWSExchangeError and mapAzureExchangeError only recognized provider 4xx faults (smithy client-fault codes, and azcore 4xx through isAzureClientError). Both go refusals are plain wrapped sentinels, so they fell through to the 500 path:

  • AWS POST /api/ri-exchange/execute. exchange.ExecuteExchange refuses a quote in a currency other than USD, or a quote with no currency, using exchange.ErrQuoteNotUSD. The refusal happens in checkInitialQuote / checkReQuote, before AcceptReservedInstancesExchangeQuote. The caller got 500 "exchange execution failed", and directExchangeFailureStatus marked the direct-execute audit row unknown, even though nothing had been submitted.
  • Azure compatible-offerings and execute. CalculateExchange refuses a source whose applied scope is missing, unsupported or Single-with-no-scope, or a set of sources with mixed scopes, using azurecompute.ErrUnsupportedAppliedScope. It does this before any Azure call. The caller got 500 "failed to find compatible offerings" or 500 "failed to price the exchange before execution".

Fix

About #361 (issue item 5)

With go#203 pinned, ExecuteExchange refuses any quote that is not in USD, on both the direct path and the approved path. So:

  • max_payment_due_usd is now only ever compared against a USD amount.
  • The ledger amount from handlerAcceptedAmount (PaymentDueUSDStr, or the USD ceiling as fallback) is USD whenever an exchange completes.

The new AWS test drives that real guardrail through the handler. On the approved path (executeApprovedExchange), the refusal already ends in failExchange with the go message as the reason, which is a clear answer, so I left it alone.

Verification

All evidence below is unit tests against fakes. No real AWS or Azure calls were made.

New and changed tests:

  • TestExecuteExchange_NonUSDQuoteRefusedAs422 (new). It runs the real go ExecuteExchange guardrail (exchange.NewExchangeClientFromAPI(fakeEC2).Execute, injected through executeExchangeFn) with an EUR quote and with a quote that names no currency. It asserts:
    • the response is 422 and the message contains "not denominated in USD"
    • accept is called zero times
    • the audit outcome is failed
  • TestAzureExchangeScopeRefused (tightened). It runs the real azurecompute.Client.CalculateExchange validation for the missing, unsupported (ManagementGroup), single-without-scopes and mixed cases, on both preview and execute. It now asserts a 422 whose message contains "unsupported applied scope" and does not contain the generic "failed to". The foreign-billing case stays 403.
  • TestMapExchangeErrors_Refusals (new). Both sentinels, wrapped, give 422 with the original message in both mappers. Provider 4xx stays 400. Other errors stay 500 with the opMsg.

Fail before. I put back the origin/main handler_ri_exchange.go and kept the new tests. 8 TestAzureExchangeScopeRefused subtests failed with expected: 422, actual: 500. TestExecuteExchange_NonUSDQuoteRefusedAs422 failed with expected: 422, actual: 500 and "exchange execution failed" does not contain "not denominated in USD". TestMapExchangeErrors_Refusals also failed.

Pass after. Commands:

go build ./...
go vet ./internal/api/
AWS_CONFIG_FILE=/dev/null AWS_SHARED_CREDENTIALS_FILE=/dev/null go test ./internal/api/ -count=1      # ok
go test ./internal/mocks/ -count=1                                                                   # ok
golangci-lint v2.10.1 run --allow-parallel-runners ./internal/api/                                    # 0 issues
gocyclo -over 10 -ignore "_test\.go" internal/api/ri_exchange_refusals.go internal/api/handler_ri_exchange.go  # silent
bash scripts/gosec-hook.sh <changed .go files>                                                        # clean
git secrets --scan (fresh git init repo, isolated HOME, --register-aws) + scripts/test-git-secrets-allowlist.sh  # clean, 42 PASS
openapi.yaml parses; TestOpenAPI* pass

I ran the tests with AWS_CONFIG_FILE=/dev/null AWS_SHARED_CREDENTIALS_FILE=/dev/null because this machine's default AWS profile is invalid.

Coverage gap. Nothing here was checked against a real AWS account billed in a currency other than USD, or a real Azure reservation with a ManagementGroup scope. The fakes return the same response shapes the go code parses.

Found but left alone

  • ErrUnsupportedPartition (go#566) is not in the pinned aws version. go#566 (92722e67) merged after aeb8a6f7. It needs a separate four-module bump, so I didn't bump go.mod here.
  • ErrNotUSD does not exist in the pinned modules. The only USD sentinel is exchange.ErrQuoteNotUSD, which is now mapped.
  • common.ErrOutcomeUnknown (accept returned no exchange ID) is reachable on AWS execute. It correctly stays a 500 with audit status unknown, because the exchange may have committed.
  • Other AWS execute refusals still return 500. The go guardrails also return plain fmt.Errorf refusals with no exported sentinel: "exchange is not valid: …", "paymentDue X exceeds max Y", "quote reported no PaymentDue", and the re-quote cap breach. Mapping them safely needs exported sentinels on the go side first; string matching would be fragile.
  • Other exported sentinels in the pinned versions are not on exchange paths: azure ErrReservationOrderBlocked (purchase), common.ErrCommitmentPurchaseNotSupported and ErrInvalidIdempotencyToken.

Frontend follow-up for the UI owner

No frontend files are changed. frontend/src/riexchange.ts (around line 2428, "Previously quoted payment: USD ${record.payment_due}") always labels a stored quote "USD". With go#203, any exchange that actually executes is in USD. But a pending record's payment_due comes from the proposal-time quote, and older history rows were written before the guard. When the record carries a currency (part of the remaining #361 platform work), the label should use it instead of the hard-coded "USD". The 422 messages added here are plain text in the standard {"error": ...} body, so the existing error toast shows them with no UI change.

Labels

A maintainer, please mirror the issue's triage labels: priority/p2, severity/medium, urgency/this-sprint, type/chore, triaged, impact/few, effort/m.

Closes #521

🤖 Generated with Claude Code

… 500

The pinned cloud-commitments-go versions already carry go#203
(exchange.ErrQuoteNotUSD) and go#209 (azurecompute.ErrUnsupportedAppliedScope),
and toAzureExchangeSources already builds sources from the owned
reservations (LeanerCloud#475). But mapAWSExchangeError and mapAzureExchangeError
only knew provider 4xx faults, so both refusals came back as a generic
500 ("exchange execution failed", "failed to price the exchange ...")
and the AWS direct-execute audit row was recorded as unknown although
the refusal happens before AcceptReservedInstancesExchangeQuote.

Both mappers now check the exported refusal sentinels first and return
a 422 carrying the go message, matching the Azure execute guardrails'
currency-mismatch 422. openapi.yaml documents the Azure list fields
(applied_scope_type, applied_scopes) and the new 422s.

Refs LeanerCloud#521

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

  • Run on-demand review

This review includes 5 billable files and costs up to $1.25.

Or wait 8 minutes for your next included review.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
Commits

Reviewing files that changed from the base of the PR and between 503eb2e and c8e1017.

Files selected for processing: 5
  • internal/api/handler_ri_exchange.go
  • internal/api/handler_ri_exchange_azure_scope_test.go
  • internal/api/openapi.yaml
  • internal/api/ri_exchange_refusals.go
  • internal/api/ri_exchange_refusals_test.go
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@IAmJSD IAmJSD added priority/p2 Backlog-worthy severity/medium Moderate harm urgency/this-sprint Within the current sprint type/chore Maintenance / non-user-visible triaged Item has been triaged impact/few Limited audience effort/m Days labels Oct 20, 2026
Resolve the conflict in internal/api/openapi.yaml: the
POST /api/ri-exchange/execute description now keeps both main's
out-of-scope 403 sentence (LeanerCloud#1171) and this branch's 422 non-USD quote
sentence.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@IAmJSD

IAmJSD commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator Author

Merged origin/main (a14dc8f2) to resolve the conflict in internal/api/openapi.yaml. There were no review findings on the previous head to address.

Conflict, and how I resolved it

New head: 2d0d8ec37d5179a00c8d14fdfb5f7ffc91e60be3

Verification on the merged tree (with AWS_CONFIG_FILE=/dev/null AWS_SHARED_CREDENTIALS_FILE=/dev/null, no cloud calls)

  • go build ./..., go vet ./internal/api/ and go vet -tags integration ./internal/api/: clean
  • go test ./internal/api/ -count=1: ok. The PR's own tests pass: TestExecuteExchange_NonUSDQuoteRefusedAs422, TestMapExchangeErrors_Refusals, TestAzureExchangeScopeRefused, and the TestOpenAPI* tests including main's TestOpenAPIReshapeOutOfScopeStatuses.
  • go test ./internal/mocks/ -count=1: ok
  • golangci-lint v2.10.1 --allow-parallel-runners on ./internal/api/, with and without --build-tags integration: 0 issues
  • gocyclo -over 10 on the two changed non-test files: silent. scripts/gosec-hook.sh: clean.
  • git-secrets: fresh repo, isolated HOME, --register-aws, changed files added. A planted fake key was flagged and the real files were clean. scripts/test-git-secrets-allowlist.sh: 137 PASS, 0 FAIL.
  • go test -tags integration ./internal/api/: no assertion failures in any test that ran, but the full suite did not finish locally.
    • The full run hit my 30m timeout after 856 top-level PASS. The VM shares Docker with other runs, so the suite runs slowly here (CI allows 20m).
    • 4 tests failed, all with the same testcontainers reaper race while starting a container (unexpected container status "removing"), not on an assertion: TestCombinedPlanPurchase_AutomaticOnScheduledExecution, TestAnalyticsScopedRawExternalIDIsProviderScoped_DB, TestUpdatePlan_CapPresencePostgres, TestPlanEditsPreserveConcurrentRampCompletion.
    • Rerun on their own, all 4 pass, along with the test cut off by the timeout and all RI-exchange integration tests (TestRIExchangeHistory_ScopedUserSeesOlderOwnRows_Integration, TestReshapeRecommendations_Integration_*, TestApproveRIExchange_Integration_SystemCreatedFourEyes). TestUpdatePlan_CapPresencePostgres hit the race again in that rerun and passed when run alone with Ryuk disabled.

No PR-body change was needed. The PR stays a draft.

🤖 Generated with Claude Code

@cristim

cristim commented Oct 25, 2026

Copy link
Copy Markdown
Member

Independent review 2 (claude-fleet, cold): APPROVE 2d0d8ec

Findings (none blocking):

  • [non-blocking, pre-existing, follow-up against cloud-commitments-go] internal/api/handler_ri_exchange.go:2000 with go pkg exchange/exchange.go:376,394 - The other pre-accept refusals in go's checkInitialQuote/checkReQuote have no sentinel:
    • "paymentDue X exceeds max Y";
    • "exchange is not valid";
    • "re-quoted payment exceeds cap".
    • They are plain fmt.Errorf, so they still map to 500 "exchange execution failed".
    • directExchangeFailureStatus then records the audit row as unknown, although accept was never called.
    • This is the same class of bug, but it is outside chore(ri-exchange): bump cloud-commitments-go and map the new refusals #521's two named sentinels, and it cannot be fixed cleanly here without an exported go sentinel.
  • [informational] chore(ri-exchange): bump cloud-commitments-go and map the new refusals #521 item 5 also named the frontend "USD" label (frontend/src/riexchange.ts:2306), which the PR leaves alone. Its argument holds: with go#203 pinned, every completed exchange has a USD quote, so the label is now correct.

Against #521:

  • The go.mod pins on origin/main (pkg 541e05e5, providers/azure 3def3f89) already contain both sentinels:
    • ErrQuoteNotUSD at pkg exchange.go:348;
    • ErrUnsupportedAppliedScope at providers/azure services/compute/exchange.go:283, with exchange_operations.go:391.
  • The PR's only behaviour change is item 2, the mapping, which was the real gap.
  • Item 1 is already on main, as the PR says (toAzureExchangeSources, TestAzureExchangeScopePreserved).

Attacked:

  • Post-commit misclassification: a 422 marks the audit row "failed", so a sentinel returned after the commit would hide a committed exchange. This cannot happen:
    • On AWS, ErrQuoteNotUSD comes only from requireUSDPaymentDue, which is called before AcceptReservedInstancesExchangeQuote (exchange.go:429-447).
    • On Azure, ExecuteExchange(sessionID) (exchange_operations.go:303) never returns ErrUnsupportedAppliedScope.
  • Call sites: the mappers are used at handler_ri_exchange.go:987, 1258, 1293, 1889 and 2000. Each is either before the commit or is the commit call itself, where the sentinel cannot occur.
  • OpenAPI:
    • The AzureExchangeableReservation fields match the go struct's JSON tags and omitempty settings.
    • The reservations wrapper matches ExchangeableAzureRIsResponse (handler:2052).
    • Both Azure endpoints already list 422, and AWS execute now gains 422.
  • Merge: git show --remerge-diff 2d0d8ec3 shows only the openapi.yaml conflict at :1487, resolved by keeping both paragraphs, with no hand edits. Since a14dc8f, main changed only go.mod among the touched files.
  • 422 body: it carries the go error text, which holds only currency codes, scope-type values, counts and source indexes.
  • Mutation probes on a scratch copy, each restored and confirmed identical with cmp. All 8 were caught:
    • M1, ErrQuoteNotUSD neutralised: the AWS 422 test and the mapper test FAIL.
    • M2, ErrUnsupportedAppliedScope neutralised: the Azure scope test and the mapper test FAIL.
    • M3, 422 changed to 400: all 3 tests FAIL.
    • M4, a generic message: all 3 FAIL.
    • M5, the AWS mapper check removed: the AWS test and the mapper test FAIL.
    • M6, the Azure mapper check removed: the scope test and the mapper test FAIL.
    • M7, 422 changed to 500: all 3 FAIL.
    • M8, errors.Is replaced by ==: all 3 FAIL, so the tests pin wrapped sentinels.

Verified locally, with GOTOOLCHAIN=go1.26.10 and no cloud credentials, under the heavy lock:

  • Merge: git merge-tree --write-tree origin/main 2d0d8ec3 is clean.
  • Merged tree:
    • go build ./... and go vet ./internal/api/: ok.
    • gofmt -l on the 4 changed files: clean.
    • go test -count=1 ./internal/api/: ok.
    • -race -run 'TestExecuteExchange_NonUSDQuoteRefusedAs422|TestMapExchangeErrors_Refusals|TestAzureExchangeScopeRefused': PASS.
  • Head: build, vet and go test ./internal/api/: ok.
  • Fail-before: with the head tests on base a14dc8f, all 3 FAIL with "expected: 422 actual: 500". These are assertion failures, not compile errors.
  • No migration, frontend or terraform changes.

Reviewed by an independent agent with no author context (second reviewer). Review only: nothing was pushed, and the GitHub review state is unchanged.

Brings in main's CI fixes (LeanerCloud#1236, LeanerCloud#1252) and CI changes since, so this PR gets a fresh CI run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cristim

cristim commented Oct 25, 2026

Copy link
Copy Markdown
Member

Independent review 2 (claude-fleet, cold), re-review: APPROVE c8e1017

This is a delta from my APPROVE at 2d0d8ec. The only new commit is a merge of origin/main (503eb2e):

  • git log --first-parent 2d0d8ec37d5179a00c8d14fdfb5f7ffc91e60be3..c8e10179d6c66b36e59fd381c1d4dd677ff3855b shows just that merge.
  • git show --remerge-diff is empty, so the merge has no hand edits.
  • The PR's own change is identical to the version I reviewed.
  • git merge-tree --write-tree origin/main c8e10179d6c66b36e59fd381c1d4dd677ff3855b is clean.

Verified locally on a git archive export of c8e1017, with GOTOOLCHAIN=go1.26.10 and no cloud credentials, under the heavy lock:

  • go build ./... and go vet ./internal/api/: ok.
  • go test -count=1 ./internal/api/: ok.
  • The PR's tests (TestExecuteExchange_NonUSDQuoteRefusedAs422, TestMapExchangeErrors_Refusals and TestAzureExchangeScopeRefused): PASS.

My earlier findings and notes still apply unchanged.

Same reviewer as the earlier review, re-reviewing the new head. Review only: nothing was pushed, and the GitHub review state is unchanged.

@cristim

cristim commented Oct 25, 2026

Copy link
Copy Markdown
Member

VERDICT: APPROVE c8e1017

Independent adversarial review of #1212 at c8e1017 (single commit 090493f on origin/main). The PR meets #521's acceptance line. go.mod on this head already pins pkg 541e05e5, providers/aws aeb8a6f7 and providers/azure 3def3f89, and those module sources contain both sentinels (pkg exchange/exchange.go:348, azure services/compute/exchange.go:283). Both refusals now return 422 through the real request builders, and the Azure sources still carry applied scope (pinned by TestAzureExchangeScopeRefused).

Findings (none blocking):

Correctness checks against the pinned go source:

  • ErrQuoteNotUSD is only produced by requireUSDPaymentDue (exchange.go:352-364). That runs from checkInitialQuote (:368) and checkReQuote (:386), and executeWithAPI calls both before AcceptReservedInstancesExchangeQuote (:447). A 422 therefore always means nothing was submitted, so recording the audit row as failed (directExchangeFailureStatus, ri_exchange_audit.go:78) is correct.
  • ErrUnsupportedAppliedScope is only produced through validateExchangeSources, which CalculateExchange calls at exchange_operations.go:236. Azure ExecuteExchange (:303) cannot return it. The post-submit mapper call at handler_ri_exchange.go:1293 therefore never turns an ambiguous submit into a 422 or a "failed" audit row.
  • The refusal check runs ahead of the smithy and azcore 4xx checks. Neither sentinel is ever wrapped together with a provider fault or with common.ErrOutcomeUnknown (classifyAcceptError only wraps accept errors), so this ordering cannot hide an outcome-unknown error.
  • The 422 body is the go error text: the source index, the applied scope type or scope count, or the quote currency code. All of it describes the caller's own request or reservation. It contains no account IDs, credentials or internal paths.
  • openapi.yaml: the AzureExchangeableReservation fields and required set match the ExchangeableReservation json tags (omitempty fields are not required). The response key reservations matches handler_ri_exchange.go:2052. The 422 entries exist on compatible-offerings, the Azure exchange endpoint and (new) /api/ri-exchange/execute, and UnprocessableEntity is defined (openapi.yaml:2598).

Attacked:

  • Targeted tests (TestExecuteExchange_NonUSDQuoteRefusedAs422, TestAzureExchangeScopeRefused, TestMapExchangeErrors_Refusals, TestOpenAPI*), then the whole internal/api package, all in a scratch export of the head: ok.
  • Mutation probes, each reverted:
    1. Remove the refusal check from mapAWSExchangeError: the NonUSD and Refusals tests FAIL.
    2. Remove it from mapAzureExchangeError: the AzureExchangeScopeRefused and Refusals tests FAIL.
    3. Return 400 instead of 422: all three tests FAIL.
    4. Return 500 with the message, so the audit row becomes unknown: all three tests FAIL.
    5. Replace exchange.ErrQuoteNotUSD in the list with a lookalike errors.New carrying the same text, so errors.Is no longer matches: the NonUSD and Refusals tests FAIL.
  • The AWS regression test drives the real go ExecuteExchange guardrail through NewExchangeClientFromAPI with an EUR quote and a quote with no currency. It asserts zero accepts and a failed audit row, so the test does not depend on a fake that hides the refusal.
  • Scope: 5 files. The handler diff is limited to the two mapper preambles and their doc comments. No go.mod change.

Not verified here: no real AWS account billed in a non-USD currency, and no real Azure reservation with a ManagementGroup scope.

@IAmJSD

IAmJSD commented Oct 25, 2026

Copy link
Copy Markdown
Collaborator Author

Independent review (IAmJSD's agent, fresh context, given only the diff, the issue and the PR text): VERDICT: APPROVE c8e10179d6c66b36e59fd381c1d4dd677ff3855b

Blocking:

  • none

Non-blocking:

  • internal/api/ri_exchange_refusals.go:36: the 422 body is the raw go error string (e.g. azure: CalculateExchange: sources[1] has a different applied scope ...). I checked the pinned go messages. They carry a scope type, a count or a currency code, and no IDs or secrets, so this is acceptable. If a future sentinel wraps provider detail, it would be echoed verbatim.
  • Issue item 5 still has a UI half: frontend/src/riexchange.ts hard-codes "USD" on stored quotes. The body documents this as a follow-up, and the acceptance line in chore(ri-exchange): bump cloud-commitments-go and map the new refusals #521 does not require it, so Closes #521 is fine.

Attacked:

  • Money safety of the 4xx (which makes directExchangeFailureStatus record failed, not unknown). In the pinned pkg@541e05e5, ErrQuoteNotUSD comes only from requireUSDPaymentDue in checkInitialQuote / checkReQuote, both before AcceptReservedInstancesExchangeQuote. Post-accept failures wrap common.ErrOutcomeUnknown and never ErrQuoteNotUSD. In providers/azure@3def3f89, ErrUnsupportedAppliedScope comes only from validateAppliedScope and the mixed-scope check inside CalculateExchange, never from ExecuteExchange. So "failed" is never recorded for an exchange that could have committed.
  • mapAWSExchangeError is used only on quote and direct execute; mapAzureExchangeError only on preview, pre-execute pricing and execute. The approved path is untouched.
  • Checked the OpenAPI AzureExchangeableReservation against the module's JSON tags and the {reservations: [...]} wrapper. They match, and UnprocessableEntity exists.
  • Fail-before: removed the two exchangeRefusalError calls from the mappers. All 8 TestAzureExchangeScopeRefused subtests, both TestExecuteExchange_NonUSDQuoteRefusedAs422 subtests and 4 TestMapExchangeErrors_Refusals subtests fail. Restored afterwards.
  • Ran on the head: go build ./...; go vet with and without -tags integration on ./internal/api/; go test -race -short -count=1 ./internal/api/ ok.
  • Ran on the head merged locally with current origin/main (merges cleanly): the same build, vet and race tests pass; golangci-lint with and without --build-tags integration reports 0 issues; gocyclo is silent.

Body-stale:

  • none

Reviewer: rA1. Bar while the maintainers are away (set by IAmJSD as acting owner): one approving independent review on the head, two for money, auth and security paths.

@IAmJSD

IAmJSD commented Oct 25, 2026

Copy link
Copy Markdown
Collaborator Author

Local verification at c8e10179d6c66b36e59fd381c1d4dd677ff3855b merged onto main (26004a6d), together with #974, #1106, #1151, #1193, #1199, #1212, #1217, #1218, #1219, #1221, #1223, #1224, #1225, #1226, #1227, #1256, #1262, #1266, #1272, #1275, #1283, #1286, #1293, #1299, #1300, #1313 in that order (combined tree d3d95c83, Linux, Go 1.26.10): go build ./..., go vet with and without -tags integration, the full go test -race -short -count=1 ./..., and go test -race -tags integration -count=1 on every changed package, against Docker Postgres. All pass. CI Success and pre-commit are green on this head, with 2 approving independent review(s) on it. Nothing was run against any cloud.

@IAmJSD
IAmJSD marked this pull request as ready for review October 25, 2026 19:37
@IAmJSD
IAmJSD merged commit bc84b2b into LeanerCloud:main Oct 25, 2026
34 checks passed
@IAmJSD

IAmJSD commented Oct 25, 2026

Copy link
Copy Markdown
Collaborator Author

Merged by IAmJSD's agent at IAmJSD's request (IAmJSD is acting owner while the maintainers are away), under the bar IAmJSD set: 2 approving independent review(s) on c8e10179 (two required: money, auth or security path), green CI, and the combined-tree verification above. Squash-merged at exactly c8e10179d6c66b36e59fd381c1d4dd677ff3855b.

@cristim

cristim commented Oct 25, 2026

Copy link
Copy Markdown
Member

Local verification at c8e10179d6c66b36e59fd381c1d4dd677ff3855b merged onto current main (5aa3df5b; merge-tree 2b2bab8e; macOS):

  • go build ./..., go vet -tags integration ./... and go test -race -short ./... pass (full suite).
  • go test -tags integration passes on real Postgres (testcontainers) for the touched packages: ./internal/api
  • Nothing was applied to any cloud.

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

Labels

effort/m Days impact/few Limited audience priority/p2 Backlog-worthy severity/medium Moderate harm triaged Item has been triaged type/chore Maintenance / non-user-visible urgency/this-sprint Within the current sprint

Projects

None yet

Development

Successfully merging this pull request may close these issues.

chore(ri-exchange): bump cloud-commitments-go and map the new refusals

2 participants