Repository navigation
fix(analytics): label snapshot commitment types from provider and service - #1302
Conversation
…failed lazy DB init on /health An execution with nothing selected never reaches a cloud API, but both the single-account and the per-account fan-out paths still sent a "purchase confirmed" email listing zero commitments. Gate the confirmation on anyRecPurchased / committed, so it goes out only when a real commitment was created (full or partial success). When reinitializeAfterConnect fails after setting app.Config, ensureDB closes the pool and clears app.DB but leaves app.Config, and the config_store health check reported healthy over the closed pool. Snapshot dbConnected with the store pointers under dbMu and report unhealthy while lazy PostgreSQL init has not completed. Audit findings A05-015 and A06-009. A06-019 (commitment_type labels) follows separately. Refs LeanerCloud#339 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…vice
commitmentTypeFor matched only a service literally named "SavingsPlans",
which purchase_history never stores, so every commitment was stored as
"RI": AWS Savings Plans ("savingsplans" / "savings-plans-<type>") and GCP
committed-use discounts included. Derive the type from (provider, service)
in the pkg/common CommitmentType vocabulary, and widen the
savings_snapshots CHECK in migration 000145 to match. The CHECK still
admits the legacy 'RI' / 'SavingsPlan' literals so an old replica can
write during a rolling deploy; existing rows are relabeled.
Audit finding A06-019.
Refs LeanerCloud#339
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…heckable by the mock guard TestNoUnfailableMockAssertions could not resolve the receiver of two AssertNotCalled sites (a mock returned by a helper). Declare the mock directly in the new test, and in the approve test rely on the missing SendPurchaseConfirmation expectation, which fails the test on any send. Refs LeanerCloud#339 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Warning Review limit reached
This review includes 10 billable files and costs up to $2.50. Or wait 59 minutes for your next included review. View limit details
Comment |
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>
|
CI fix pushed. New head: Root cause. The unit test
The guard is syntactic, so it reports a call result it cannot type as unchecked. Fix. #1269 (
Because this PR is stacked on #1269, I merged #1269's current head Other changes: I merged Checks run locally on the new head:
|
|
Independent review (IAmJSD's agent, fresh context, given only the diff, the issue and the PR text): VERDICT: REQUEST_CHANGES Blocking:
Non-blocking:
Attacked:
Body-stale:
Reviewer: rB20. 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. |
…ent-type Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LeanerCloud#1254 (on main) replaced the hand-rolled config-store mock in collector_test.go and dropped the now-unused pkg/common import. This branch's new commitment-type assertion uses common.CommitmentType, so the analytics test build broke after main was merged in although the merge was textually clean. Same assertion. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Addressed the review at |
|
Independent review (IAmJSD's agent, fresh context, given only the diff, the issue and the PR text): VERDICT: APPROVE Blocking:
Non-blocking:
Attacked:
Body-stale:
Reviewer: rB23. 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. |
|
Independent review 1 (cold, fresh context): VERDICT: APPROVE 337d25e Findings:
Attacked:
|
|
Independent review 2 (claude-fleet, cold): REQUEST_CHANGES at 337d25e blocking
non-blocking
label table (provider / slug / label / source of truth / test)
migration analysis
consumer table
probes (head export; each file restored with cp and checked with cmp, and the three probed files were then cmp'd against git show 337d25e)
fail-before: I copied head's tests into base, with a shim for the new commitmentTypeFor(provider, service) signature. TestCollectorCollect (2 subtests), TestCommitmentTypeLogic and TestValidateCommitmentType FAIL. TestCollector_GCPCommittedUse_DB FAILS (actual: all "RI"). The 000145 test FAILS at MigrateToVersion(145). history: the first-parent chain is 337d25e (adds only the common import, needed and correct) on 6513cf5, ba15e4a, 55a2753 and ae4f340 (merges). --remerge-diff is empty for all four merges, so there were no hand resolutions. commands (no cloud creds, GOTOOLCHAIN=go1.26.10)
Reviewed by an independent agent with no author context (second reviewer). Review only: nothing was pushed, and the GitHub review state is unchanged. |
|
On the two reviews at 337d25e: our first review flagged the same default as a nit (an unknown provider or service, and the dash-free @IAmJSD the fix the second review suggests is enough:
Merge order is unchanged: 000145 lands after 000140 to 000144. |
…ation 000145 -> 000141 golang-migrate only applies versions above the database's current one, so PRs must merge in migration-number order. 000140-000144 had been taken by PRs that are now held on owner decisions (LeanerCloud#1239, LeanerCloud#1241, LeanerCloud#1253). The ready PRs move to 000140-000145 so they can merge ahead of them, and the held ones will take the next free numbers when decided. Rename only: the SQL statements are unchanged, and the test version constants and the comments naming the migration follow the new number. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Renumbered migration 000145 -> 000141 so it can merge ahead of PRs held on owner decisions (#1239, #1241, #1253, which will take the next free numbers when decided). Rename only; SQL unchanged. New head: Note: #1269, which this PR was stacked on, has merged, so the diff now shows only this PR's own change. Merge order for the ready PRs: #1240 (000140) -> #1302 (000141) -> #1268 (000142) -> #1287 (000143) -> #1289 (000144) -> #1116 (000145). The PR body's migration-number paragraph is updated to match. Checks on this head (local): build, vet (plain, integration, e2e), golangci-lint v2.10.1 with and without the integration tag and gocyclo -over 10 on the changed packages, |
|
Independent review (IAmJSD's agent, fresh context, given only the diff, the issue and the PR text): VERDICT: APPROVE Blocking:
Non-blocking:
Attacked:
Body-stale:
Reviewer: rE1. 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. |
|
Local verification at |
|
Merged by IAmJSD's agent at IAmJSD's request (IAmJSD is acting owner while the maintainers are away), under the bar IAmJSD set: 1 approving independent review(s) on |
Stacked on #1269 (fix/issue-339). This branch starts from #1269's first commit, so until #1269 merges the diff also shows that commit. Review only the last commit,
73bdb19d. It is split out because it needs a migration, while #1269 doesn't.Refs #339
Root cause (audit finding A06-019)
commitmentTypeFor(internal/analytics) only recognised a service named exactly"SavingsPlans", whichpurchase_historynever stores. So every snapshot was labelled"RI", including AWS Savings Plans (savingsplans/savings-plans-<type>) and GCP committed-use discounts. The audit only mentioned GCP; AWS Savings Plans were mislabelled too.Fix
internal/analytics/commitment_type.go: derives the type from (provider, service) using thepkg/commonCommitmentTypevocabulary. The collector uses it, and the old literal match inpostgres_analytics.gois removed.savings_snapshots_commitment_type_vocabulary): widens thesavings_snapshotsCHECK to the new vocabulary and relabels existing rows. The CHECK still accepts the legacyRIandSavingsPlanliterals, so an old replica can keep writing during a rolling deploy.RIagain.Migration order
Migration number: 000141 (renumbered from 000145 on 2026-10-26). main is at 000139. The reviewed, ready PRs take 000140-000145 and merge in this order: #1240 (000140) → #1302 (000141) → #1268 (000142) → #1287 (000143) → #1289 (000144) → #1116 (000145). golang-migrate skips a version below the database's current one, so merge this after #1240 and before #1268. #1239 (was 000140-000141), #1241 (was 000143) and #1253 (was 000144) are held on owner decisions and will be renumbered to start at 000146 when decided.
Verification
Branch evidence: unit tests (
collector_test.go,postgres_analytics_test.go, mock test updates), a real-Postgres test (commitment_type_db_test.go) and a migration test (000141_..._test.go) covering the relabel and the CHECK.Combined-tree verification. This branch was tested together with the other queued branches on a local, never-pushed tree:
queue/platform@936996ad= origin/maina14dc8f2plus these heads, merged in this order:fix/issue-72andfix/issue-312conflict on one line ininternal/api/handler_dashboard.go(both change thecalculateCommitmentMetricscall). The tree used #72's side with #312's validatedprovidervariable; whichever PR merges second takes that same resolution.Linux, Go 1.26.9,
AWS_CONFIG_FILE=/dev/null AWS_SHARED_CREDENTIALS_FILE=/dev/null AWS_EC2_METADATA_DISABLED=true; Docker Postgres via testcontainers:936996ad:go build ./...,go vet ./...,go vet -tags integration ./...and the fullgo test -race -short -count=1 ./....936996ad: golangci-lint v2.10.1 found 0 issues on every changed package, with and without--build-tags integration.tests/e2eis a separate module, so it was vetted under its owne2etag.gocyclo -over 10is silent on the changed non-test files.-tags integration -count=1, 60-minute local timeout because the VM was shared):936996ad, these pass:internal/server,internal/credentials,internal/scheduler,internal/database/postgres/migrations,internal/purchaseandinternal/api.internal/configpasses exceptTestPostgresStoreDB_ResolveStaleRIExchange_ValidatesCloudCheck, which fails the same way on unmodified main (fixed by fix(config): compare resolve checked_at at the database's microsecond precision #1252).26795376, same branches at earlier heads):internal/analytics,internal/auth,internal/commitmentopts,internal/databaseandcmd/lambda/clear-rate-limitall pass. The later fixes don't touch those packages.httpToLambdaRequestsignature change;scripts/setup-git-secrets.shpatterns in a fresh repo with an isolated HOME, found nothing in the changed files, and a planted fake key was flagged.scripts/test-git-secrets-allowlist.sh: 137 PASS, 0 FAIL.Review manually
pkg/common'sCommitmentTypelabels are the right ones to store.RIandSavingsPlanin the CHECK during the rollout, and when to remove them in a later migration.RI.Labels to mirror from #339: triaged, priority/p3, severity/low, urgency/eventually, impact/few, effort/m, type/bug.
🤖 Generated with Claude Code