Skip to content

feat(bom): raw-SKU line items for estimate_bom and compare_bom_regions (RC3-004, #31) - #96

Merged
x7even merged 1 commit into
mainfrom
rc3-004-sku-item-bom-lines
Jul 7, 2026
Merged

feat(bom): raw-SKU line items for estimate_bom and compare_bom_regions (RC3-004, #31)#96
x7even merged 1 commit into
mainfrom
rc3-004-sku-item-bom-lines

Conversation

@x7even

@x7even x7even commented Jul 7, 2026

Copy link
Copy Markdown
Owner

Summary

Progresses #31 (RC3-004). Scoped to raw-SKU line items only (v1: AWS-only, synchronous per-region) — weighting and a providers[] filter are deferred to a follow-up issue, per the original issue's own DACI-listed fallback scope ("(a) Build v1 AWS-only, synchronous per-line calls").

  • processBOMItems (shared by estimate_bom and compare_bom_regions) now accepts a raw-SKU dict alongside PricingSpec dicts, resolving it via the same AWS SKU resolver get_price_by_sku already uses (LookupSKUAcrossRegions).
  • compare_bom_regions's partition loop routes raw-SKU items through the same AWS-only gate as PricingSpec items, reporting a non-AWS raw-SKU item once in not_supported rather than once per compared region.
  • Raw-SKU BoM line items now correctly feed BOMAdvisories (egress/LB/NAT/backup rows) by mapping the AWS Pricing API servicecode to the short-form token BOMAdvisories expects.
  • Field-level validation (provider/service/operation/product_family) now distinguishes "absent" from "wrong type," surfacing a clear error instead of silently defaulting.
  • Extracted shared helpers (rawBOMSKU, stringItemField, classifySKURegionResult, resolveAWSSKUProviderFromMap) so this logic isn't duplicated between bom.go and sku_lookup.go.

This also gives #33 (RC3-014, blocked on RC3-004) its proposed unblock mechanism: DynamoDB and other CUR-native services that fail estimate_bom's PricingSpec Supports() check can now be passed as raw-SKU items instead.

Ran through /code-review (high effort, 8 finder angles + verification) against the implementation; this PR includes fixes for all 9 confirmed/plausible findings from that review (duplicate not_supported errors, broken advisory matching, untrimmed SKU whitespace, silent type-coercion on hint fields, duplicated SKU-result discrimination logic, dropped error codes, stale tool description).

Test plan

  • go build ./...
  • go vet ./...
  • go test ./... (all packages, including TestDescriptionParityWithSnapshot/TestOutputSchemaParityWithSnapshot)
  • gofmt -l on all touched files (clean)
  • New regression tests: whitespace-trimmed SKU, non-AWS-provider raw-SKU item reported once (not per-region), non-string provider/operation rejected with a clear error, raw-SKU EC2 item still populates BOM advisories
  • Independent re-verification pass confirming each of the 9 code-review findings is fixed in the current source (not just self-reported)

…ions (RC3-004, #31)

Route CUR-style usage-type/SKU strings through processBOMItems (shared by
estimate_bom and compare_bom_regions) via the same AWS SKU resolver
get_price_by_sku already uses, so both tools accept a raw-SKU item
alongside PricingSpec dicts. Scoped to raw-SKU support only; weighting and
a providers filter are deferred.
@x7even
x7even marked this pull request as ready for review July 7, 2026 00:02
@x7even
x7even merged commit 3e3e7a7 into main Jul 7, 2026
1 check passed
@x7even
x7even deleted the rc3-004-sku-item-bom-lines branch July 7, 2026 00:02
x7even added a commit that referenced this pull request Jul 7, 2026
get_price_by_sku and get_prices_by_sku already emit tiered/all_tier_rates
for GCP usage-volume-tiered SKUs and document it in their descriptions, but
the OutputSchema for both tools never declared the fields. Found during
merge-conflict review while reconciling PR #96 (raw-SKU BoM line items) with
PR #97 (GCP SKU lookup parity).
x7even added a commit that referenced this pull request Jul 9, 2026
* feat(bom): add raw-SKU line items to estimate_bom and compare_bom_regions (RC3-004, #31)

Route CUR-style usage-type/SKU strings through processBOMItems (shared by
estimate_bom and compare_bom_regions) via the same AWS SKU resolver
get_price_by_sku already uses, so both tools accept a raw-SKU item
alongside PricingSpec dicts. Scoped to raw-SKU support only; weighting and
a providers filter are deferred.

* feat(gcp): add raw-SKU lookup parity with AWS (RC3-015, #35)

Extends get_price_by_sku, get_prices_by_sku, and the raw-SKU branches of
estimate_bom/compare_bom_regions to support provider="gcp", matching AWS's
existing raw-SKU lookup. Hoists the shared lookup types/interface into a new
internal/skulookup package so both providers implement one contract.

GCP's region attribution follows a geoTaxonomy-first, serviceRegions-fallback
rule (GLOBAL/REGIONAL/MULTI_REGIONAL), since restrictive serviceRegions lists
on otherwise-global SKUs (observed in KMS) make plain serviceRegions matching
unreliable on its own. Checked all 13 onboarded GCP services for
MULTI_REGIONAL geoTaxonomy usage before generalizing Firestore's multi-region
short-name parser — only Firestore needs it, so it stays Firestore-specific
rather than being hoisted into a shared abstraction prematurely.

Also fixes graduated tiered-rate billing math, region-name case sensitivity,
a GCP catalog-fetch cache-stampede risk (singleflight coalescing), and removes
dead code left behind by the provider-generic refactor, found via an 8-angle
review pass over the initial implementation.

* fix(server): declare tiered/all_tier_rates in SKU-lookup output schemas

get_price_by_sku and get_prices_by_sku already emit tiered/all_tier_rates
for GCP usage-volume-tiered SKUs and document it in their descriptions, but
the OutputSchema for both tools never declared the fields. Found during
merge-conflict review while reconciling PR #96 (raw-SKU BoM line items) with
PR #97 (GCP SKU lookup parity).

* docs(sku-lookup): fix stale AWS-only doc comments after GCP generalization

sku_lookup.go, bom.go, and aws_sku_lookup.go still described raw-SKU
lookup as an AWS-only concept in doc comments and one user-facing error
message, left over from before RC3-015 generalized it to also support
provider="gcp". Comment/string-only changes, no logic touched.

* feat(azure): add raw-SKU lookup parity with AWS/GCP (RC3-015 Azure)

Implements Azure as a third provider behind the shared
skulookup.SKULookupProvider interface: fetch once by meterId (no
per-region calls), bucket by ArmRegionName, disambiguate by
IsPrimaryMeterRegion then type/spot-meterName hints, with a
collision-safe tier resolver that reports Ambiguous rather than
guessing when Reservation-type rows share identical
(meterId, region, type, tierMinimumUnits) but price differently -
confirmed against live API data where 86/115 sampled Reservation
groups collided this way.

Wires provider="azure" through get_price_by_sku, get_prices_by_sku,
estimate_bom, and compare_bom_regions raw-SKU items.

* fix(server): populate MCP structuredContent on every tool response

Every tool declares an OutputSchema, but the shared jsonText response
helper only ever set Content (unstructured text), never
StructuredContent - a pre-existing gap since the original Go server
rewrite. Strict MCP clients (the official Python SDK, used by the
local test harness) reject every tool call outright when a schema is
declared but structuredContent is absent, which surfaced only now
under harness testing.

Also defaults two nil []string fields (get_price_by_sku/get_prices_by_sku's
no_mapping_in[].attempted_services on Azure's no-mapping path, and the
upstream_failure branch's regions when the caller omits regions) to
empty arrays before they reach the response map - both marshal to JSON
null otherwise, which fails schema validation since both fields are
declared as non-nullable arrays.

* test(harness): add 71 raw-SKU-lookup coverage tests across AWS/GCP/Azure

Covers the 7 gaps flagged in code review: AWS raw-SKU success, GCP/Azure
no-mapping, ambiguous match, GCP/Azure tiered rates, batch mixed outcomes,
raw-SKU BoM line items, and protocol edge cases (missing region, empty
batch, unsupported provider, oversized batch, blank SKU).

Adds rsku_manifest.json alongside TEST_PROMPTS since the harness has no
per-test expected-outcome schema; the manifest carries expected_outcome
per test ID for post-run grading.

* fix(bom): require explicit provider for raw-SKU BoM items

resolveBOMSKUItem (bom.go) silently defaulted a missing provider to
"aws", risking misrouted lookups since BoM calls routinely mix items
from multiple providers in one request. Now returns a clear error
instead. compare_bom_regions.go's independent item-partitioning loop
gets the matching fix, using stringItemField so a non-string provider
value is reported as a type error rather than folded into "missing",
and a shared notSupportedEntry helper plus rawSKUProviderRequiredHint
constant to keep the two call sites' messages from drifting apart.

Also updates the stale field-level items schema description (server.go
and tools-snapshot.json) that still said "provider aws or gcp" and
omitted the required-provider behavior, and strengthens the regression
test to reuse the existing makeComputePrice fixture helper.

* chore(build): bump go toolchain to 1.25.12 to fix GO-2026-5856

go1.25.11's crypto/tls has an Encrypted Client Hello privacy leak
(GO-2026-5856), flagged by CI's govulncheck step. Fixed upstream in
go1.25.12.
@x7even x7even mentioned this pull request Jul 9, 2026
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant