Repository navigation
Description
Context
Part of #19. The audit in docs/audits/api-adapter-audit-2026-11-09.md (audited commit 72d90d54b06769710b7c49094365892d9a4d4ab0) found no new data-loss bug beyond those already fixed (#1788, #1905, #1906) or tracked (#1776, #923). It leaves three kinds of follow-up work, listed below. No code change was in scope for the audit.
This issue is unclaimed on purpose: pick any item, one commit per item or group.
Round-trip test here means a test that asserts every field of the pair at the callee boundary (fixture store, SQL or outbound client), not only the converter's output. Pair ids (A1, B2, E5, ...) are the ones used in the audit.
1. Pairs with no round-trip test
- A1
CloudAccountRequest-> createAccount:internal/api/handler_accounts.go:357, converter:548, storeinternal/config/store_postgres.go:4049. Only the converter guard exists. - A2
CloudAccountRequest-> updateAccount:handler_accounts.go:626, converter:655, storestore_postgres.go:4200. - A3
CloudAccountRequest-> createSelfAccount:handler_accounts.go:284-296. - A4
CredentialsRequest-> saveAccountCredentials:handler_accounts.go:802,SaveCredentialat:839; the test asserts success only. - A5
DiscoverOrgRequest-> discoverOrgAccounts:handler_accounts.go:1661. - R1
RegistrationRequest-> submitRegistration:internal/api/handler_registrations.go:91; credential fields are allowlisted in the guard. - R2
CloudAccountRequest-> approveRegistration:handler_registrations.go:352; add a case with an explicitenabledfalse. - R3
RejectRequest-> rejectRegistration:handler_registrations.go:402. - P1
{account_ids}-> setPlanAccounts:handler_accounts.go:1461; the test asserts success only. - B1
PlanRequest-> createPlan:internal/api/handler_plans.go:168; onlySetPlanAccountsarguments are asserted. - B2
PlanRequest-> updatePlan:handler_plans.go:319,:327; the update path fordescriptionandtarget_accountsis unguarded (fix(api): request fields accepted and silently ignored (#19 audit follow-ups) #1776). - B5
config.ServiceConfig-> updateServiceConfig:internal/api/handler_config.go:499. - E1
ExchangeQuoteRequestBody-> getExchangeQuote:internal/api/handler_ri_exchange.go:1956;targets[].count,ri_idsandtarget_counthave no assertion. - E2
ExchangeExecuteRequestBody-> executeExchange:handler_ri_exchange.go:2029; only the audit row is asserted, not the library request. - E3
AzureCompatibleOfferingsRequestBody-> getAzureCompatibleOfferings:handler_ri_exchange.go:981; the Azure call is mocked withmock.Anything. - E4
AzureExecuteExchangeRequestBody-> executeAzureExchange:handler_ri_exchange.go:1222; only the audit row is asserted. - E5
RIExchangeConfigUpdateRequest-> updateRIExchangeConfig:handler_ri_exchange.go:2305; no stored-value assertion for any of the six fields. - E9
ExecutePurchaseRequest-> executePurchase:internal/api/handler_purchases.go:3233;capacity_percentpersistence and theexecute_modebranch. - U1
LoginRequest-> login:internal/api/handler_auth.go:30,internal/server/app.go:1178-1184;TestLogin_MFATokenRoundTripassertsmfa_tokenonly. - U3 to U10 (
PasswordResetRequest,PasswordResetConfirm,ProfileUpdateRequest,ChangePasswordRequest, MFA setup, enable, disable, regenerate):handler_auth.go:414,:505,:545,:649,:719,:743,:764,:789; only handler-boundary argument tests exist, and the adapter smoke tests cover error paths. - Consider extending
requireFieldsReach(internal/api/adapter_field_drop_test.go:96-122) to the exchange, resolve and settle request types, or add one reflection guard forauthServiceAdapter(internal/server/app.go:1178-1251).
2. Deliberately ignored fields with no source comment: add a comment
- Service-config PUT
providerandservicein the body are overwritten by the URL path:internal/api/handler_config.go:510-514. - Ladder config
created_atandupdated_atare accepted and replaced byNOW():internal/api/handler_ladder.go:102,internal/config/store_postgres_ladder.go:105,118. -
acknowledge_unknown_outcomeon outcomecompletedis accepted and not stored:internal/api/handler_ri_exchange_resolve.go:43-45,:247-249;handler_ri_exchange_direct_resolve_request.go:14-17,:73-74. The comments state the requirement forfailedonly. -
PlanRequest.descriptionand the update-pathtarget_accounts:internal/api/types.go:890-907,handler_plans.go:431-469. Only the test allowlist (adapter_field_drop_test.go:145-146) mentions them. Skip if fix(api): request fields accepted and silently ignored (#19 audit follow-ups) #1776 is fixed first. -
ExecutePurchaseRequest.recommendations[]fields replaced from the stored row rely on one shared comment (handler_purchases.go:3146-3150); optional per-field pointer forcapacity_percentandexecute_mode.
3. Dead fields: remove the field
-
CreateUserRequest,UpdateUserRequest,CreateGroupRequest,UpdateGroupRequest:internal/api/types.go:377,:384,:417,:425. Referenced by no Go code; live decode targets areauth.APICreateUserRequest,APIUpdateUserRequest,APICreateGroupRequest,APIUpdateGroupRequest(internal/auth/service_api.go:93,:124,:131,:151). - OpenAPI
roleon theCreateUserRequestandUpdateUserRequestschemas:internal/api/openapi.yaml:6723,:6741. No decode type has it (removed in fix(analytics): partition bounds depend on session TimeZone (DST shifts month boundaries) #912).
Other findings
-
PUT /api/configacceptsri_exchange_utilization_threshold0 and stores 95:internal/config/validation.go:351-353,internal/config/store_postgres.go:333-336. The comment atvalidation.go:332-335claims parity with the RI exchange route, which fix(api): reject an unknown ramp_schedule and a zero utilization_threshold #1906 changed. Proposed fix: reject 0 or store it, and make the comment true. -
Marketplace
price_schedule[].term_monthsandpriceare not traced intocloud-commitments-go:internal/api/handler_marketplace_attempt.go:83,:199-204. Proposed fix: assert the fields on the fake EC2 client input in the existing test (handler_marketplace_attempt_test.go:441) or add a test in the module. -
acknowledge_unknown_outcomeoncompleted(E6, E7): decide whether a true value should be rejected with 400 instead of ignored,handler_ri_exchange_resolve.go:237-249,handler_ri_exchange_direct_resolve_request.go:73-74. If it is rejected, the comment item above is not needed. -
internal/server/handler.go:453ScheduledEventis classed as not an adapter, yet it copies fields intoScheduledTaskParams;source,detail-typeanddetailare unused after the decode (handler.go:451-484). Proposed fix: a one-line comment at the decode. -
Ladder config
provideris accepted but not forwarded to the engine, whose scope is hard-coded to AWS:internal/server/handler_ladder.go:598. Proposed fix: reject a non-AWS provider or comment it. -
PUT /api/plans/{id} resets
notification_days_before,enabledandauto_purchasewhen omitted (internal/api/types.go:857,864,865) and ignores a loneproviderorservice(types.go:954-956). Proposed fix: say so ininternal/api/openapi.yaml, or return 400 for a loneproviderorservice. -
Plain
json.Unmarshaldecoders accept unknown keys:handler_ri_exchange.go:981,:1222,:1956,:2029,:2305,handler_purchases_revoke.go:296,handler_purchases.go:3233; the resolve, direct-resolve and settle decoders reject them. Proposed fix: decide onDisallowUnknownFieldsfor the exchange bodies (a client typo in a cap field is currently ignored). -
Azure
billing_scope_idon execute is recorded in the audit targets JSON with no test:internal/api/ri_exchange_audit.go:251. Proposed fix: add an audit-row assertion for a spelled-out scope. -
The OpenAPI response schemas
UserInfo,CurrentUserResponseandUser(internal/api/openapi.yaml) still listrole, which no Go response type emits (auth.APIUser,internal/auth/service_api.go:23-33);Useralso lacksactive,invite_email_sentandinvite_email_error. Check the login and current-user handlers, then align the three schemas.
Activity
cristim commentedon Nov 10, 2026
Taking this in slices, one PR each. Slice 1: sections 2 and 3 (the missing source comments and the dead request types and OpenAPI
role). Later slices: the round-trip tests by area, then the other findings.chore(api): drop dead request types and document ignored fields (#1912)
test(api): assert the RI exchange quote and execute library requests (#…
cristim commentedon Nov 10, 2026
Slices 1 and 2 are merged (#1912, #1916): the ticked items. The remaining items are independent and open to pick up (one commit per item or group); I am moving on to other work and releasing the claim.
cristim commentedon Nov 10, 2026
Claimed by claude-qa-ui-3 again (the coordinator asked me to take the remaining items): working them one at a time, each a direct commit. Order: E5 (stored-value assertions for the RI exchange config update), the stale OpenAPI response
roleentries, then E9 (execute purchase persistence). R2 is already covered by the #1905 test.test(api): assert every field the RI exchange config update stores
docs(api): align the user response schemas with what the API returns
test(api): assert executePurchase persists capacity_percent
docs(api): document what updating a plan replaces and what ScheduledE…
test(api): assert credentials, plan accounts and rejection reasons re…
test(api): assert the service config update stores every overlay field
test(api): assert the plan a create request stores
test(api): assert what a plan update resets and keeps
fix(api): refuse acknowledge_unknown_outcome on a completed RI exchan…
docs(ladder): say why the engine scope is always AWS
chore(adapter): assert Azure billing scope in exchange audit row (#1907)
chore(adapter): document PlanRequest.target_accounts is create-only (#…
test(adapter): account create/update/self round-trip (#1907)
test(adapter): registration submit round-trip (#1907)
test(adapter): Azure exchange offerings and execute round-trip (#1907)
test(adapter): login, password and MFA round-trip (#1907)
cristim commentedon Nov 14, 2026
Claimed by claude-platform-2 (coordinator assignment 2026-11-14): premise-check against main first, then finish and close (or narrow with the leftovers named).