You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The automatic-rebalancer cron has failed on every run since 2026-09-17 with Insufficient USDC on Base. Have: 1032.95, need: 2000.
Two defects stacked:
A run from 2026-09-07 is still persisted as in flight. It reached squidRouterApproveAndSwap, the Polygon swap could not be sent (no POL for gas), and the BRLA was swapped back manually the same day. The state file in Supabase Storage was never updated, so it still expects ~5184 BRLA on Polygon.
The opportunistic in-range path could never resume it. It quoted, sized, and balance-checked a fresh USDC→BRLA→USDC run before the state machine looked at the stuck one. With the in-flight USDC gone from the wallet, that check failed every run. The out-of-range path had its own resume check; the opportunistic path had none.
What
checkForRebalancing now resumes a non-idle state file for either Base flow before any quote, sizing, daily-limit, or balance check. The duplicated isResuming branches in runUsdcToBrla / runBrlaToUsdc are removed. --restart semantics are unchanged.
The profitable USDC→BRLA amount is only selected when the Base USDC balance can fund it; otherwise the standard amount is used instead of crashing after two quote round-trips.
bun run reset:usdc-base-state (dry-run by default, --confirm to reset to idle keeping history) for runs whose funds were reconciled manually. README and security-spec invariants updated.
Ops step after merge
The code fix will resume the stale state and fail loudly on the missing Polygon BRLA until that state is reset once (bun run reset:usdc-base-state --confirm with the cron's Supabase env, or set state.currentPhase to idle in the Supabase UI).
Tests
apps/rebalancer/src/rebalanceCycle.test.ts covers the resume-before-fresh-evaluation ordering (coverage read first, nothing fresh after a resume), the dry-run pause, the --restart bypass, both Base flows' non-idle states, and the profitable-amount gate (off mode and equal amounts never read the balance, unaffordable amounts fall back to the standard size). The orchestration was moved out of index.ts into rebalanceCycle.ts behind injected dependencies to make that possible. Rebalancer suite (78 tests), typecheck, lint, and the coverage ratchet pass.
The opportunistic in-range path quoted, sized, and balance-checked a fresh
USDC->BRLA->USDC run before the state machine got a chance to resume a run
that died mid-flow. With the in-flight USDC no longer in the wallet, that
balance check failed on every cron run and the stuck state was never
resumed. The out-of-range path had its own resume check; hoist a single
one to the top of checkForRebalancing for both Base flows.
Also stop selecting the profitable USDC->BRLA amount when the Base USDC
balance cannot fund it: fall back to the standard amount instead of
crashing after two quote round-trips.
Dry-run by default: prints the persisted USDC->BRLA->USDC state from
Supabase Storage. With --confirm it resets the phase to idle while keeping
history, for runs whose funds were reconciled manually.
…ming
Resuming a persisted run in dry-run mode would write state and move funds
during an invocation the spec defines as read-only, and resuming before the
coverage read let funds move when the indexer read would have failed.
Both were latent on the out-of-range paths before the resume was hoisted.
The reset script and a live run write the same Supabase object without
locking, and the script's closing message contradicted the README's
reconcile-first order.
Continuing into the fresh evaluation quoted and sized a run that assumes
the in-flight USDC is still in the wallet, and logged "no rebalancing
needed" while a run was paused.
Avoid wallet-balance RPC when rebalancing policy is off
apps/rebalancer/src/index.ts:275
When REBALANCING_POLICY_MODE=off and the profitable amount differs from the standard amount, the standard policy decision is non-executing but control still reaches this new wallet-balance RPC. That violates the documented off-mode invariant (docs/security-spec/07-operations/rebalancer.md:255) and can make a disabled cron fail on an unnecessary balance read. Return the standard skip decision before reading the balance in off mode, and cover this configuration with a regression test.
The affordability gate read the wallet balance even when the policy mode
is off, which the spec defines as returning before any balance read.
The resume-before-fresh-evaluation ordering, the dry-run pause, the
--restart bypass and the profitable-amount gate had no automated
coverage because index.ts runs the cycle at import. Move that
orchestration into rebalanceCycle.ts behind injected dependencies so a
later refactor cannot silently reorder it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The
automatic-rebalancercron has failed on every run since 2026-09-17 withInsufficient USDC on Base. Have: 1032.95, need: 2000.Two defects stacked:
squidRouterApproveAndSwap, the Polygon swap could not be sent (no POL for gas), and the BRLA was swapped back manually the same day. The state file in Supabase Storage was never updated, so it still expects ~5184 BRLA on Polygon.What
checkForRebalancingnow resumes a non-idle state file for either Base flow before any quote, sizing, daily-limit, or balance check. The duplicatedisResumingbranches inrunUsdcToBrla/runBrlaToUsdcare removed.--restartsemantics are unchanged.bun run reset:usdc-base-state(dry-run by default,--confirmto reset to idle keeping history) for runs whose funds were reconciled manually. README and security-spec invariants updated.Ops step after merge
The code fix will resume the stale state and fail loudly on the missing Polygon BRLA until that state is reset once (
bun run reset:usdc-base-state --confirmwith the cron's Supabase env, or setstate.currentPhasetoidlein the Supabase UI).Tests
apps/rebalancer/src/rebalanceCycle.test.tscovers the resume-before-fresh-evaluation ordering (coverage read first, nothing fresh after a resume), the dry-run pause, the--restartbypass, both Base flows' non-idle states, and the profitable-amount gate (off mode and equal amounts never read the balance, unaffordable amounts fall back to the standard size). The orchestration was moved out ofindex.tsintorebalanceCycle.tsbehind injected dependencies to make that possible. Rebalancer suite (78 tests), typecheck, lint, and the coverage ratchet pass.