Skip to content

πŸ› οΈ SKILL: long-sbtc-leverage-looper#606

Closed
TheBigMacBTC wants to merge 6 commits into
mainfrom
day20-sbtc-leverage-looper-rebuild
Closed

πŸ› οΈ SKILL: long-sbtc-leverage-looper#606
TheBigMacBTC wants to merge 6 commits into
mainfrom
day20-sbtc-leverage-looper-rebuild

Conversation

@TheBigMacBTC
Copy link
Copy Markdown
Contributor

@TheBigMacBTC TheBigMacBTC commented May 12, 2026

Skill Submission

Skill name: sbtc-leverage-looper
Category: Yield
HODLMM integration? Yes β€” every swap pins to dlmm-swap-router-v-1-2 on the canonical 15-bps STX/sBTC pool.

Edit 2026-05-12T17:33:47Z (re: #606 (review)): HODLMM integration? No β€” DLMM router used as swap venue only, not as an LP destination. Aligning with the precedent set on #604 β€” swap-venue routing isn't the qualifying integration for the bonus pool.


PR Rebuilt and Resubmitted on behalf of @daraijaola:
[AIBTC Skills Comp Day 20] sbtc-leverage-looper β€” first leveraged sBTC long on Stacks via Zest + Bitflow HODLMM
#348

author: "daraijaola"
author-agent: "Grim Wand"

Added 2026-05-12T17:33:47Z (re: #606 (review)):

Scope of this rebuild

This PR's scope was the swap-leg constants in the original on #348. Those three Bitflow constants pointed to deployer SP2F4QC563WN0A0949WPH5W1YBTSC5AV92B2S3KY β€” a 40-char address whose c32 checksum fails (valid Stacks addresses are 41 chars), so Hiro returns HTTP 400 on every read. The wstx-token id SP1Y5YSTAHZ88XYK1VPDH24GY0HPX5J4JECTMY4A1.wstx-token is Hiro 404. Every swap call site in the original would have failed before reaching broadcast.

Fixed by pinning to the canonical SM1FKXGNZJWSTWDWXQZJNF7B5TV5ZB235JTCXYXKD.dlmm-swap-router-v-1-2 on the 15-bps STX/sBTC pool, with all 7 Clarity args (pool-trait + x-token-trait + y-token-trait + amount + min-out + max-steps + deadline-optional) verified against the router's on-chain source.

Architectural gap (acknowledged)

The skill emits JSON contract-call literals for MCP-wallet execution β€” it does not directly broadcast via @stacks/transactions, does not persist checkpoint state across legs, does not wait for tx_status: success on Hiro between contract calls, and does not enforce its safety constants post-broadcast.

This is the same pattern that disqualified the original on #348 per the PRD review. The swap-leg constants fix in this rebuild does not address that disqualification by itself. The full broadcast / checkpoint architecture β€” composing zest-asset-deposit-primitive (#574), zest-borrow-asset-primitive (#572), and bitflow-swap-aggregator (upstream aibtcdev/skills#360) via Bun.spawn, plus a state machine for the 3-leg loop and 3-leg unwind with resume-from-any-step β€” is a separate effort.

This PR is therefore best read as: (a) a swap-leg correctness fix to the original on #348, restoring the contract-call literals to valid mainnet principals, and (b) a planner / composer reference for the leveraged-long primitive. The write-skill broadcast variant will ship as a separate PR.


What it does

The first skill on Stacks that lets an AI agent build and manage a leveraged long sBTC position using existing DeFi protocols.

The loop is simple and composable:

User supplies sBTC to Zest (collateral)
  β†’ Agent borrows STX against collateral (Zest borrow)
  β†’ Agent swaps STX β†’ sBTC via the Bitflow DLMM router (HODLMM-pinned, bonus eligible)
  β†’ Agent deposits received sBTC back to Zest (increases collateral)
  β†’ Repeat to target leverage ratio

To deleverage (unwind):

Agent withdraws sBTC from Zest
  β†’ Agent swaps sBTC β†’ STX via the Bitflow DLMM router (HODLMM-pinned)
  β†’ Agent repays STX debt to Zest

Every swap routes through dlmm-swap-router-v-1-2 pinned to the canonical STX/sBTC 15-bps pool (dlmm-pool-stx-sbtc-v-1-bps-15). HODLMM is the marketing name for DLMM β€” same product β€” so every loop and unwind directly integrates HODLMM on every operation.

Commands

Command Type Description
doctor read Pre-flight: wallet, APIs, Zest position, gas, daily cap, circuit breaker
status read Position snapshot: leverage ratio, HF, liquidation price, carry P&L, HODLMM APY vs borrow rate
loop write One leverage iteration β€” borrow β†’ swap β†’ supply (--confirm=LOOP)
unwind write One deleverage step β€” withdraw β†’ swap β†’ repay (--confirm=UNWIND)
run write Autonomous β€” loops to target leverage, auto-unwinds if HF below trigger (--confirm=RUN)

All write commands support --dry-run.

Flags

Flag Commands Description
--confirm=LOOP loop Required exact token
--confirm=UNWIND unwind Required exact token
--confirm=RUN run Required exact token
--dry-run loop, unwind, run Simulate β€” full preview, no execution, no state change
--target-leverage=N loop, run Target leverage ratio (default 1.5, hard max 2.5)
--slippage=N loop, unwind, run Swap slippage tolerance % (default 1.0, hard max 5.0)
--repay-all unwind Repay 100% of debt in one unwind (default 50%)

Safety constants (hardcoded β€” not configurable at runtime)

Edit 2026-05-12T17:33:47Z (re: #606 (review)): Advisory in the current architecture β€” see "Architectural gap" section above. The skill plans contract calls; an operator who bypasses the JSON envelope to invoke the MCP wallet directly can ignore these gates. The constants become enforceable once the broadcast / checkpoint variant ships.

Constant Value Purpose
MAX_LTV_PCT 55% Max borrowed per loop as % of collateral
HF_FLOOR_HARD 1.5 Absolute hard stop β€” refuse any write action
HF_FLOOR_LOOP 1.8 Min HF before adding another leverage loop
HF_UNWIND_TRIGGER 1.65 Auto-unwind threshold in run
MAX_LEVERAGE_CAP 2.5x Hard ceiling on --target-leverage
MAX_BORROW_STX_PER_OP 5,000 STX Per-operation borrow cap
MAX_DAILY_STX 20,000 STX Rolling 24h cap across all operations
COOLDOWN_HOURS 4 Minimum between loop/unwind operations
MAX_SLIPPAGE_PCT 5.0% Slippage ceiling
MAX_ERRORS_24H / CIRCUIT_BREAKER_HRS 3 / 24h Circuit-breaker thresholds

Output contract

All commands return strict JSON:

{
  "status": "success | error | blocked",
  "action": "doctor | status | loop | unwind | run",
  "data": {},
  "error": null
}

Write actions include numbered contract-call steps (step1ContractCall, step2ContractCall, step3ContractCall) for execution via the AIBTC MCP wallet.

Example usage

# Health check
bun sbtc-leverage-looper.ts doctor

# Position snapshot with carry analysis
bun sbtc-leverage-looper.ts status

# Dry-run before committing
bun sbtc-leverage-looper.ts loop --dry-run

# Open first leverage loop (1.5x target, 1% slippage)
bun sbtc-leverage-looper.ts loop --confirm=LOOP --target-leverage=1.5

# Autonomous management β€” loops or unwinds as needed
bun sbtc-leverage-looper.ts run --confirm=RUN --target-leverage=1.5

# Full unwind β€” repay all debt
bun sbtc-leverage-looper.ts unwind --confirm=UNWIND --repay-all

Added 2026-05-12T17:33:47Z (re: #606 (review)):

On-chain proof

Not yet attached. The current architecture emits a plan rather than broadcasting; an end-to-end mainnet proof is structurally tied to the broadcast / checkpoint rewrite tracked in the "Architectural gap" section above. Once that variant ships, mainnet tx hashes will follow.

daraijaola and others added 6 commits April 14, 2026 10:47
…bps pool

The original three Bitflow constants (HODLMM_STX_SBTC, BITFLOW_ROUTER,
BITFLOW_SWAP) all pointed to deployer "SP2F4QC...2B2S3KY" β€” a 40-char
address whose c32 checksum fails. Hiro returns HTTP 400 "invalid STX
address" for every read, so all four swap call sites (cmdLoop @1151,
cmdUnwind @1273, run-unwind @1426, run-loop @1457) would have failed on
broadcast. The skill also tried to fetch quotes against
"SP1Y5YST...wstx-token" which is Hiro 404, so every live quote fell
through to a USD-price-based fallback that fabricated a "HODLMM
(estimated)" route label.

Canonical Stacks mainnet primary-source verification (Hiro 200 on each):
  SM1FKXGNZJWSTWDWXQZJNF7B5TV5ZB235JTCXYXKD.dlmm-swap-router-v-1-2
  SM1FKXGNZJWSTWDWXQZJNF7B5TV5ZB235JTCXYXKD.dlmm-pool-stx-sbtc-v-1-bps-15

The router's swap-x-for-y-simple-range-multi and
swap-y-for-x-simple-range-multi entrypoints take seven Clarity args
each: (pool-trait <dlmm-pool-trait>) (x-token-trait <sip-010-trait>)
(y-token-trait <sip-010-trait>) (x-amount/y-amount uint)
(min-dy/min-dx uint) (max-steps uint) (deadline-time (optional uint)).
The pool's get-pool read returns x-token =
SM1793C4R5PZ4NS4VQ4WMP7SKKYVH8JZEWSZ9HCCR.token-stx-v-1-2 (the
canonical wSTX wrapper) and y-token = sbtc-token.

Changes:
- Delete HODLMM_STX_SBTC, BITFLOW_ROUTER, BITFLOW_SWAP.
- Add DLMM_ROUTER, STX_SBTC_POOL, WSTX_TOKEN, MAX_SWAP_STEPS = 230
  (3 + 230 * 52 = 11,963 reads, under the router's 15k ceiling).
- All four swap call sites now contractCall(DLMM_ROUTER, ...) with
  swap-x-for-y for STX -> sBTC and swap-y-for-x for sBTC -> STX,
  passing the explicit pool + token traits + amount + min-out +
  max-steps + (deadline-time = none). Every swap pins to the DLMM
  pool, preserving HODLMM bonus eligibility on every operation.
- getBitflowSwapQuote rewritten: uses the corrected wSTX token id;
  drops the price-fallback estimator; drops the unused route field
  from the return type. If the API is unreachable the loop now
  refuses (status=error) rather than ship a fabricated route.
- swapRoute display value is now the static "DLMM 15-bps STX/sBTC pool"
  since the route is hardcoded.
- Header doc + three CLI subcommand descriptions reframed from
  "Bitflow HODLMM" pool to "Bitflow DLMM router" (HODLMM-pinned).

Co-Authored-By: IJAOLA MICHAEL OLUWADARASIMI <daraijaola8@gmail.com>
…r swap leg

- Frontmatter descriptions: pool-pinned to DLMM router on canonical
  STX/sBTC 15-bps pool; HODLMM bonus eligibility called out explicitly.
- "What it does" loop + unwind diagrams: HODLMM pool framing β†’ DLMM
  router (HODLMM-pinned) framing.
- Protocol-contracts table: replace fabricated bitflow-hodlmm-stx-ststx
  + swap-helper-v-1-1 rows with the canonical SM1FKXG... DLMM router +
  pool + wSTX wrapper. Full principals included.
- Refusal condition #7 reworded β€” the price-based fallback estimator
  has been removed from the code, so the dual-path framing no longer
  applies.
- Loop + unwind step breakdowns rewritten to show the 7-arg
  swap-x-for-y-simple-range-multi / swap-y-for-x-simple-range-multi
  call shape: pool-trait + x-token-trait + y-token-trait + amount +
  min-out + max-steps + deadline-time(optional).

Co-Authored-By: IJAOLA MICHAEL OLUWADARASIMI <daraijaola8@gmail.com>
@github-actions
Copy link
Copy Markdown

βœ… Validation Passed

Skill: sbtc-leverage-looper
Errors: 0
Warnings: 2

All checks passed. This submission is ready for review.

@TheBigMacBTC TheBigMacBTC changed the title [AIBTC Day 20] sbtc-leverage-looper β€” rebuild of #348 by @daraijaola (DLMM router swap) [AIBTC Day 20] sbtc-leverage-looper β€” rebuild of #348 May 12, 2026
@TheBigMacBTC TheBigMacBTC changed the title [AIBTC Day 20] sbtc-leverage-looper β€” rebuild of #348 [AIBTC x Bitflow Skill] sbtc-leverage-looper β€” rebuild of #348 May 12, 2026
@TheBigMacBTC TheBigMacBTC changed the title [AIBTC x Bitflow Skill] sbtc-leverage-looper β€” rebuild of #348 πŸ› οΈ SKILL: sbtc-leverage-looper May 12, 2026
@TheBigMacBTC TheBigMacBTC changed the title πŸ› οΈ SKILL: sbtc-leverage-looper πŸ› οΈ SKILL: long-sbtc-leverage-looper May 12, 2026
@TheBigMacBTC TheBigMacBTC requested a review from diegomey May 12, 2026 05:10
Copy link
Copy Markdown
Contributor

@diegomey diegomey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review at HEAD ca3b0bdd

This is a thorough re-read against the current competition standard for write skills. CI passes structurally (frontmatter / file layout / JSON contract) but there are blocking issues at the implementation and bonus-eligibility levels that need to be addressed before this can move past COMMENTED.


Critical: the skill does not actually broadcast β€” it emits instructions

I grep'd the 1,079-line TS file for the markers of an actual write skill:

Pattern Count
broadcastTransaction 0
makeContractCall 0
@stacks/transactions (import) 0
step1ContractCall / step2ContractCall / step3ContractCall 4 each
contractCall( (builder) 13

The skill does not import @stacks/transactions, does not call makeContractCall, does not call broadcastTransaction, and does not sign or submit anything to Hiro. Write actions return a JSON envelope with step1ContractCall / step2ContractCall / step3ContractCall objects describing what the operator's MCP wallet should execute elsewhere.

This is acknowledged in the PR body itself:

"Write actions include numbered contract-call steps (step1ContractCall, step2ContractCall, step3ContractCall) for execution via the AIBTC MCP wallet."

This is the exact pattern that disqualified #348 per the #562 PRD deep-dive finding β€” and #348 is the PR this submission says it rebuilds from:

"outputs ordered contract-call instructions instead of executing per-leg... does not prove an executed unwind on mainnet... does not provide durable checkpoint/resume"

The entire competition has moved past instruction-emission. Look at any of the write skills that landed in the last 4 weeks for the current bar:

Skill Broadcast discipline
#494 hodlmm-inventory-balancer makeContractCall + broadcastTransaction + waitForTx + checkpoint persist + tx_status:success gate. Two complete on-chain rotation cycles.
#585 bitflow-funding-coordinator Same. Plus nonce-manager serialization, --target-out enforcement, post-broadcast catch-path nonce-state semantics. Mainnet proof tx 0x766e90d3.
#588 sbtc-leverage-unwind-planner Same. Plus 16-state machine with resume-from-any-step, residual-debt gate, partial-cycle blocking.
#604 windleg Same. Plus v6/v7 SDK adaptation, Clarity decoder fixes, full 5-leg rotation. 5 mainnet tx proofs.
#605 unwindleg Same. Plus Hermetica silo claim-id snapshot, 7-day cooldown checkpoint durability, residual-debt gate, post-cooldown resume.

Every one of these:

  • Imports @stacks/transactions
  • Calls makeContractCall to build a transaction
  • Calls broadcastTransaction to submit it
  • Waits for tx_status: success on Hiro
  • Persists checkpoint state to recover from crashes
  • Provides mainnet tx hashes as proof

This PR does none of that. It builds JSON describing what could be done.

This is a hard blocker for being reviewed as a write skill. The competition rules require "actually broadcasts (not just returns unsigned tx)" β€” and the prior PRD review for the leverage-loop class of skills explicitly singled out instruction-emission as a disqualification count.


Critical: HODLMM bonus eligibility β€” overclaimed

"The swap leg routes through HODLMM dlmm_8 because it is the only available USDh venue on Bitflow, but the skill does not LP into HODLMM as a destination. Per the bonus criterion ('skills that directly integrate HODLMM'), the qualifying integration is LP/destination, not swap-venue routing. Declared honestly rather than padded for the bonus pool."

This PR uses HODLMM the same way #604 does β€” as a swap venue, not as an LP destination β€” and claims bonus eligibility.


Critical: No on-chain proof

There is no ## On-chain proof section in the PR body. No transaction table. No explorer links. No tx_status: success evidence. Per the BFF rules (#484 Β§5):

"Write skill: mainnet tx hash included, sender matches my registered wallet (verifiable via agent-lookup), contract/function matches claim, tx_status: success"

This is impossible to provide with the current architecture because the skill never broadcasts. Even running it end-to-end produces a JSON envelope, not a tx hash. The pre-submission checklist box that all the other write skills check is structurally unattainable here.


Substantive: Safety constants document intent but cannot be enforced

The Safety constants table lists:

  • HF_FLOOR_HARD = 1.5 β€” "Absolute hard stop β€” refuse any write action"
  • MAX_LTV_PCT = 55%
  • MAX_BORROW_STX_PER_OP = 5,000 STX
  • MAX_DAILY_STX = 20,000 STX
  • COOLDOWN_HOURS = 4
  • MAX_ERRORS_24H / CIRCUIT_BREAKER_HRS = 3 / 24h

These are reasonable thresholds on paper. But:

  • The skill doesn't broadcast, so it can't read post-broadcast HF
  • The skill doesn't persist checkpoints across operator MCP-wallet executions, so it can't enforce cooldown across operations
  • The skill doesn't count errors from actual broadcasts, so the circuit breaker can't fire
  • Daily caps would have to track operator-executed txs, which this skill doesn't observe

These are all gateable conditions on instruction generation β€” but an operator who invokes the MCP wallet directly is free to ignore them. The safety constants are advisory, not enforced, in the current architecture.


Substantive: Composition pattern

The skill is shaped as a single 1,079-line monolith that internally encodes Zest borrow/supply/withdraw/repay logic and Bitflow swap routing. The current competition norm (set by #585 / #588 / #604 / #605) is to compose primitives via subprocess (Bun.spawn) and skill metadata.requires:

  • zest-asset-deposit-primitive exists in the registry (#574 β€” merged) β€” this skill reimplements supply-collateral-add inline
  • zest-borrow-asset-primitive exists (#572 β€” merged) β€” this skill reimplements borrow inline
  • bitflow-swap-aggregator exists (aibtcdev/skills#360 β€” merged) β€” this skill reimplements swap routing inline
  • No zest-repay or zest-withdraw primitive yet (per #605 review notes), so those would still need to be inline

By not composing the existing primitives, this skill misses the safety surfaces those primitives already provide (post-conditions, slippage gates, registry validation, etc.). And it duplicates the work, so when those primitives gain new features (residual-debt gates, ABI corrections like #604's decodeClarityBool fix or #605's inlineUnstake arg-count fix), this skill won't benefit.


Verdict

COMMENTED (no formal vote). This is request-changes territory, but I'm reading it as comment-only to give the author a clear path forward rather than blocking outright.

Path to make this reviewable as a write skill:

  1. Replace the step1ContractCall / step2ContractCall / step3ContractCall pattern with actual broadcasts. Import @stacks/transactions, use makeContractCall + broadcastTransaction, wait for tx_status: success on Hiro between legs. Look at #585 / #604 / #605 for the broadcast wrapper pattern (signer resolver, v6/v7 SDK adaptation, dual-arg broadcast signature, BroadcastContext shape).

  2. Persist checkpoint state. A leveraged loop is a multi-leg operation across N iterations; you need a state machine with loop_N_borrow_broadcast / borrow_confirmed / swap_broadcast / swap_confirmed / supply_broadcast / supply_confirmed so the skill survives crashes mid-loop.

  3. Compose existing primitives where they exist. Use zest-asset-deposit-primitive (#574), zest-borrow-asset-primitive (#572), and bitflow-swap-aggregator (aibtcdev/skills#360) via Bun.spawn. Inline only the things no primitive covers (Zest repay, Zest collateral-remove). This reduces the 1,079 LOC substantially and aligns with the composition convention set by #483.

  4. Either provide actual on-chain proof or remove "write" tag. A write skill requires tx_status: success evidence. As-is, this is a planning/dry-run tool that previews a leveraged long, not a tool that executes one.

  5. Reconcile HODLMM bonus claim. Align with #604's precedent: decline the bonus for swap-venue routing, or articulate why this case differs.

  6. Acknowledge the architectural debt to #348. This PR is explicitly framed as a rebuild of #348, which was PRD-rejected for instruction-emission. Re-shipping the same architecture under a new title doesn't address the rejection. The PR body should either commit to porting to inline broadcasts or be reframed as a planner/composition reference rather than a write skill.


Useful context worth knowing

The leverage-cycle and unwind-planner controllers are already being built properly by other contributors in this competition:

  • #578 sbtc-leverage-cycle-controller (forward) β€” merged
  • #588 sbtc-leverage-unwind-planner (unwind) β€” in review with extensive checkpoint state machine
  • #604 windleg (sBTC β†’ sUSDh) β€” 5-tx mainnet proof landed
  • #605 unwindleg (sUSDh β†’ sBTC) β€” Phase 1 broadcast pending, Phase 2 7d later

These are the controllers that the #473 / #559 leverage loop / yield rotator roadmap is being built around. This PR is genuinely interesting as a design document for a leveraged long primitive (the safety constants table is well-thought-out) but, as code, it's at least 4 weeks behind the current bar and re-introduces an architecture that was explicitly disqualified.

If the goal is to land this skill, the cleanest path is to fork-and-restart on the broadcast-discipline pattern that #604/#605 demonstrate. If the goal is to publish the design, file an issue or PRD instead and the inline-broadcast implementation can land separately.


I'd rather signal this clearly now than have it sit unreviewed for two weeks like #348 did. Happy to do a more detailed code-review pass once the broadcast architecture is in place.

Code review generated by Claude Code on behalf of @diegomey.


Generated by Claude Code

@TheBigMacBTC
Copy link
Copy Markdown
Contributor Author

Acknowledging the points raised in #606 (review):

HODLMM bonus claim β€” corrected

Aligning with the precedent set in #604 β€” swap-venue routing is not the qualifying integration; LP / destination is. The original "HODLMM integration? Yes" in the PR body is overclaimed. Updating the body to: HODLMM integration? No β€” DLMM router used as swap venue only, not as an LP destination.

Architecture β€” instruction-emission preserved; broadcast / checkpoint rewrite deferred

You're right that the skill emits JSON contract-call literals for MCP-wallet execution rather than broadcasting via @stacks/transactions. That architecture was preserved verbatim from #348 β€” the scope of this PR was the swap-leg constants only. The original's three Bitflow constants pointed to a c32-invalid deployer (SP2F4QC... β€” 40 chars, fails checksum, Hiro 400), and the wstx-token id was Hiro 404, so every swap would have failed at the broadcast layer regardless of what was wrapping it.

Re-shipping the overall architecture from #348 under a new title doesn't address the PRD rejection that disqualified it in the first place. That's a fair call.

The full broadcast / checkpoint architecture β€” makeContractCall + broadcastTransaction + waitForTx on Hiro between legs + state machine across the 3-leg loop + 3-leg unwind with loop_N_borrow_broadcast β†’ confirmed β†’ swap_broadcast β†’ confirmed β†’ supply_broadcast β†’ confirmed transitions + checkpoint persistence + resume-from-any-step β€” is a separate effort. Two paths from here:

(a) Land this PR as a planner / composer reference for the leveraged-long primitive, with the architectural debt called out explicitly in the body, then ship the broadcast variant as a separate PR composing the existing primitives.

(b) Hold this PR and do the broadcast rewrite in place.

Will reply again once we've decided.

Composition β€” primitives over inline is the right shape

Acknowledged. zest-asset-deposit-primitive (#574), zest-borrow-asset-primitive (#572), and bitflow-swap-aggregator (aibtcdev/skills#360 β€” upstream merged) are the right primitives to compose via Bun.spawn rather than reimplementing inline. Zest repay + collateral-remove don't have primitives yet (per the review notes on #605), so those remain inline for the broadcast variant. Deferred to the rewrite.


Closing this PR in preference to the composition-pattern controllers already being built β€” #578 (sbtc-leverage-cycle-controller, merged), #588 (sbtc-leverage-unwind-planner), #604 (windleg), and #605 (unwindleg). Those adopt the inline-broadcast + checkpoint architecture the review correctly identifies as the current bar. The leveraged-long primitive belongs in a composition of zest-asset-deposit-primitive (#574) + zest-borrow-asset-primitive (#572) + bitflow-swap-aggregator (upstream aibtcdev/skills#360), not in this inline-only planner.

@TheBigMacBTC
Copy link
Copy Markdown
Contributor Author

Follow-up after commit a38999d:

Fix: retry-with-backoff on transient 429 / network error in fetchJson (the shared fetch helper used by doctor's zest+hodlmm checks and the loop's price/quote reads). 4-attempt linear backoff (5s/15s/30s, max ~50s total). Existing return shape unchanged β€” all callers get the retry for free.

Why: Doctor's zestApi: false + "Zest API unreachable" was a false negative on a single 429 from the per-minute /v1/pools bucket β€” the API is actually healthy and the bucket refreshes in seconds. The patch keeps doctor green across normal bucket churn instead of blocking the loop's entry path on transient throttling.

Same defensive pattern just landed in PR #604 (checkHermeticaStakingEnabled, commit 581cec7) β€” see the architectural note at #604 (comment) for the broader composable-skill family context (PR #604 windleg + PR #605 unwindleg + future orchestrator). The 429 false-negative class is the same shape across all three skills' external API surface β€” patching at the central fetch helper closes the variant in this loop.

bun --target=bun --no-bundle transpile clean. No behavioral change when the bucket is healthy (the backoff never fires); pure defensive code.

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.

4 participants