Skip to content

feat(ladder): report the net existing commitment Allocate sized the gap against - #614

Draft
IAmJSD wants to merge 1 commit into
LeanerCloud:mainfrom
IAmJSD:fix/issue-83
Draft

IAmJSD wants to merge 1 commit into
LeanerCloud:mainfrom
IAmJSD:fix/issue-83

Conversation

@IAmJSD

@IAmJSD IAmJSD commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator

This finishes the go part of the 2026-07-28 review bundle. Two earlier PRs already fixed three of the four items. This PR fixes the last one (the go half of the cross-repo Existing item) and corrects a doc comment that #605 left stale.

Closes #83

Status of each finding on current main (03f40a3)

# Finding Status
1 capOrUnlimited maps a nil reshape cap to math.MaxFloat64 on the auto path (providers/aws/ladder/reshape.go) Already done in #605 (205cf99): requireCap rejects nil and unlimitedCapUSD is gone. Checked on main: reshape.go:100-135. #605 left the doc on ladder.BufferReshapeConfig saying "nil means no cap"; this PR corrects it (pkg/ladder/capability.go).
2 pkg/exchange runs any mode other than "manual" as auto Already done in #578 (7da716a): RunAutoExchange accepts only ExchangeModeAuto / ExchangeModeManual and rejects anything else before any store or exchange call (pkg/exchange/auto.go:199-203).
3 A failed CancelPendingExchangesByOrigin is logged and ignored, which leaves two live approval tokens Already done in #578 (7da716a): cancelStalePending returns the error and the run stops before it creates any exchange (pkg/exchange/auto.go:218-246).
4 Cross-repo: the ladder plan reports gross Existing, but Allocate sizes the gap against net (go half: expose the net figure from Allocate) Done here (go half). The platform half is platform#243 and needs this module version first.

Root cause and fix (item 4)

Allocate sizes the gap against net existing commitment (existing minus expiring, from computeNetExisting), but it never returned that figure. The platform's assembleLadderPlan therefore adds up gross ExistingUSDPerHour from the layer states. The plan's Existing line in the approval email then doesn't reconcile with its target and gap.

  • AllocateResult gains a new NetExistingUSDPerHour *big.Rat field. It holds the exact E the gap was sized against and is set on every successful result, including the target-met and baseline-unavailable holds. The platform reports existing on those holds too.
  • Allocate is split into a thin outer function (validate, compute net existing, then call the inner allocate, then set the field) and the unchanged steps 2-8 in allocate. That way every return path gets the field without touching each early exit, and it stays clear of the lines that fix(ladder): stop subtracting existing commitment from residual baselines #441 rewrites (see Overlap).
  • Behavior change: net existing is now computed before the baseline-unavailable hold. A snapshot whose expiring figure exceeds its existing figure, combined with a nil baseline, now returns the existing "inconsistent provider snapshot" error instead of a hold. Both outcomes buy nothing. The error is the repo's fail-loud rule for inconsistent provider data, and it means the hold can never report an existing figure built from a bad snapshot.

Verification

All of this is unit-level evidence with in-memory fixtures. No cloud APIs and no DB are involved; the change is pure computation.

Fail-before: I committed the new field without the code that sets it, then ran go test -count=1 -run 'NetExisting|NilBaselineInconsistent' ./ladder/ in pkg:

  • TestAllocate_ReportsNetExisting/{allocation,target_met_hold,baseline_unavailable_hold}: FAIL, NetExistingUSDPerHour is nil
  • TestAllocate_NetExistingReconcilesWithGap: FAIL, NetExistingUSDPerHour is nil
  • TestAllocate_NilBaselineInconsistentSnapshot_Error: FAIL, expected an error for expiring > existing with a nil baseline, got nil

Pass-after: all 5 pass. The fixture has $10/hr existing on one layer, of which $4/hr expires, plus $2/hr on a second layer. Gross existing is $12/hr and net is $8/hr. The reconcile test checks that planned OD gap + NetExistingUSDPerHour equals the $20/hr target, which a gross figure would fail ($24 ≠ $20).

Commands (with AWS_CONFIG_FILE=/dev/null AWS_SHARED_CREDENTIALS_FILE=/dev/null AWS_EC2_METADATA_DISABLED=true):

CI-equivalent run on the branch head (e669581d, up to date with go main 03f40a3; Linux, Go 1.26.9, AWS_CONFIG_FILE=/dev/null AWS_SHARED_CREDENTIALS_FILE=/dev/null AWS_EC2_METADATA_DISABLED=true). It's the only go branch in this round, so the combined tree is this branch on main:

  • go test -mod=readonly -race -short -count=1 ./... passes in each module: pkg, providers/aws, providers/azure, providers/gcp.
  • pkg integration tests (go test -race -tags=integration -short -count=1 ./...) pass.
  • git-secrets with the repo's scripts/setup-git-secrets.sh patterns, in a fresh repo with an isolated HOME: the changed files are clean and a planted fake key was flagged. scripts/test-git-secrets-allowlist.sh: 68 PASS, 0 FAIL.

Overlap with our open PRs

Out of scope

  • The platform half of item 4 (assembleLadderPlan reports allocResult.NetExistingUSDPerHour instead of summing gross layer states) is platform#243. It needs a pkg version that includes this change.
  • I did not expose per-layer net existing (the nets map). The platform plan only reports a total, so I left it internal.

Please mirror the issue's triage labels onto this PR: triaged, type/chore, effort/l, priority/p3, severity/low, urgency/eventually, impact/few.

Note: the commit message says Refs #83. The PR body uses Closes #83 because, with #578 and #605 already merged, this PR completes the go part of the issue.

🤖 Generated with Claude Code

…ap against

Allocate sizes the purchase gap against net existing commitment (existing
minus expiring), but the result did not expose that figure, so callers
recomputed a gross one from the layer states and a plan's existing figure
did not reconcile with its gap. AllocateResult now carries
NetExistingUSDPerHour on every successful result, including the hold-only
early exits. Net existing is computed right after validation, before the
baseline-unavailable hold, so an inconsistent snapshot (expiring above
existing) is now an error on that path too instead of a hold.

Also correct the BufferReshapeConfig cap docs: since LeanerCloud#605 a nil cap is
rejected, not treated as "no cap".

Refs LeanerCloud#83

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

coderabbitai Bot commented Oct 23, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

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.

chore(purchase/ladder/exchange): medium/low findings from the 2026-07-28 full review [go part]

1 participant