feat(clmm): manage_clmm tool to recover orphaned LP positions; bin_count on get_pool_info - #204
Open
fengtality wants to merge 13 commits into
Open
feat(clmm): manage_clmm tool to recover orphaned LP positions; bin_count on get_pool_info#204fengtality wants to merge 13 commits into
fengtality wants to merge 13 commits into
Conversation
This was referenced Aug 13, 2026
Greptile SummaryThe PR adds CLMM orphan-position recovery, exposes orphan lifecycle information through executor tooling, wires
Confidence Score: 5/5The PR appears safe to merge after its explicitly documented dependency release prerequisite is satisfied. No blocking failure remains; the previously reported
|
| Filename | Overview |
|---|---|
| mcp_servers/hummingbot_api/schemas.py | Adds unconditional routing for orphan actions, explicit CLMM request fields, and the tested fix for missing resolve_orphan IDs. |
| mcp_servers/hummingbot_api/tools/executors.py | Adds orphan discovery and resolution flows while surfacing already-terminated executor state and recovery guidance. |
| mcp_servers/hummingbot_api/tools/gateway_clmm.py | Adds direct CLMM position-management dispatch, validation, connector normalization, and pool-address forwarding. |
| condor/agents/providers/executors.py | Exposes terminal executors that may retain on-chain positions and provides reason-aware recovery instructions. |
| agents/derive_options_trader/routines/options_flow.py | Introduces the Derive options signal routine used by the renamed trading agent. |
| uv.lock | Updates the locked API client requirement to the acknowledged unreleased 1.5.8 dependency. |
Sequence Diagram
sequenceDiagram
participant Agent
participant Executors as manage_executors
participant CLMM as manage_clmm
participant Gateway
Agent->>Executors: "action="orphaned""
Executors-->>Agent: position, pool, connector, network
Agent->>CLMM: "action="close" with position and pool"
CLMM->>Gateway: close_position(...)
Gateway-->>CLMM: close result
CLMM-->>Agent: position closed
Agent->>Executors: "action="resolve_orphan", executor_id"
Executors-->>Agent: orphan marked recovered
Reviews (13): Last reviewed commit: "test(agents): teach the numeric-credenti..." | Re-trigger Greptile
fengtality
added a commit
that referenced
this pull request
Aug 13, 2026
… error Greptile P1 on #204: get_flow_stage() required executor_id for the resolve_orphan action, so a call missing the id silently fell through to show_schema/list_types and the recovery request was ignored. The action now always routes to resolve_orphan and the tool returns an explicit error pointing at action="orphaned" to find candidates. Also documents in the LP executor guide that resolve_orphan updates the API database only — an lp_rebalancer controller's in-memory orphan halt clears on controller restart, so restart after resolving. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Want your agent to iterate on Greptile's feedback? Try greploops. |
This was referenced Aug 13, 2026
Merged
Contributor
Local hummingbot-api-client v1.5.8 integration ✅
Test still in progress |
Contributor
Contributor
|
Test update:
Test orphan recovery acknolwedgement
Telegram stop handling for an already-terminated orphan
|
fengtality
added a commit
that referenced
this pull request
Aug 17, 2026
… error Greptile P1 on #204: get_flow_stage() required executor_id for the resolve_orphan action, so a call missing the id silently fell through to show_schema/list_types and the recovery request was ignored. The action now always routes to resolve_orphan and the tool returns an explicit error pointing at action="orphaned" to find candidates. Also documents in the LP executor guide that resolve_orphan updates the API database only — an lp_rebalancer controller's in-memory orphan halt clears on controller restart, so restart after resolving. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fengtality
force-pushed
the
feat/lp-close-retry-ownership
branch
from
August 17, 2026 17:54
add49f5 to
02c8d94
Compare
3 tasks
Contributor
Author
…t warnings, orphaned/resolve_orphan actions Condor-side of the gateway#678 retry-ownership work (canonical design: docs/retry-architecture.md in the companion gateway PR). Agents drive executors through manage_executors, where no controller exists to react to a stranded position — and the tick-prompt summary was RUNNING-only, so a terminal executor holding a live on-chain position was invisible: - ExecutorsProvider surfaces terminal executors that still own a position (involuntary POSITION_HOLD with hold_reason, the injected orphaned_position flag, or legacy FAILED-with-position) as a reason-aware ORPHANED POSITION warning with explicit recovery guidance: close via the gateway tools by position address — a fresh lp_executor CANNOT adopt an existing position and would mint a second one — then mark recovered. Also exposed as orphaned_executors in provider data. - manage_executors gains orphaned (list recovery candidates) and resolve_orphan (mark recovered) actions wired to the new API endpoints. - stop handler surfaces the already_terminated payload (close_type, position_address, orphan warning) instead of treating it as a plain stop; Telegram stop menu shows the same. - guides/lp_executor.md: close-exhaustion now terminates as an involuntary POSITION_HOLD (hold_reason=close_retries_exhausted), FAILED means nothing-left-on-chain, and the orphan recovery flow. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HahKfEY9rvKnZijrzUAFSq
… error Greptile P1 on #204: get_flow_stage() required executor_id for the resolve_orphan action, so a call missing the id silently fell through to show_schema/list_types and the recovery request was ignored. The action now always routes to resolve_orphan and the tool returns an explicit error pointing at action="orphaned" to find candidates. Also documents in the LP executor guide that resolve_orphan updates the API database only — an lp_rebalancer controller's in-memory orphan halt clears on controller restart, so restart after resolving. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Regression cover for d8012bd: a resolve_orphan call with no executor_id must route to the resolve_orphan stage and return an actionable error, rather than falling through get_flow_stage() to show_schema/list_types and answering a recovery request with executor-type listings. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Agents can now request the per-tick liquidity distribution around the active price via manage_gateway_clmm(action="get_pool_info", bin_count=N), which the companion API forwards to Gateway. Meteora always returns its bins; orca, raydium, uniswap and pancakeswap compute them on request, so the default of 0 keeps pool-info cheap. The client library's get_pool_info has no bin_count parameter, so requests with bins go straight to the endpoint — the same passthrough the executors tools use for newer routes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1.5.8 adds bin_count to gateway_clmm.get_pool_info, so the tool calls the typed client method instead of reaching past it to the endpoint. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The orphan warning told the agent to "close the position via the gateway tools (remove liquidity by position address)" — an instruction with no implementing tool. Following it, an agent found that manage_executors(action="stop") is a no-op (the executor has already terminated, which is the correct contract), manage_amm handles AMMs only, and explore_dex_pools is read-only. The position stayed open and the recovery loop dead-ended. Adds manage_clmm, mirroring manage_amm: progressive-disclosure guide, per-action validation, dispatch to client.gateway_clmm.*. Actions: position_info, open, add_liquidity, remove_liquidity, close, collect_fees. Pool discovery stays in explore_dex_pools rather than being duplicated. Two details decide whether a recovery call actually works, and both are covered by tests: - an orphan records its DEX as lp_provider "orca/clmm" while Gateway routes on the bare "orca", so the connector is normalised - an lp_executor position is not in the API database, so close must forward pool_address or the API returns 400 The orphan listing now emits the concrete call rather than prose, built from the record's own fields — including that connector_name holds the network, not the DEX, which is the easiest way to construct a dead call by hand. Records still awaiting reconciliation emit no call, so no close is ever suggested with an unknown position address. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mt84XBEMVxbbyMG8fDxDKj
Four places told the agent (or the Telegram user) to close an orphaned position "via the gateway tools" — the tick-prompt warning, the Telegram stop menu, the manage_executors docstring, and the orphan listing. None named a tool that could do it, and the nearest guess, stopping the executor, is a no-op because it has already terminated. All four now name manage_clmm(action="close") and say plainly that stopping will not close the position. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mt84XBEMVxbbyMG8fDxDKj
pyproject already required ==1.5.8, but uv.lock still resolved 1.5.6, so every
`uv run --frozen` silently re-synced the venv back down. That downgrade is not
cosmetic: 1.5.6 has no pool_address on close_position and no add/remove
liquidity, so manage_clmm's close died with
TypeError: close_position() got an unexpected keyword argument 'pool_address'
The lock now names 1.5.8. Its sdist/wheel hashes are absent because 1.5.8 is not
published yet — they get filled in by a plain `uv lock` once hummingbot-api-client#25
merges and releases. `uv lock --check` accepts the entry as consistent with
pyproject in the meantime.
Until that release, `uv run --frozen` fails loudly:
Distribution `hummingbot-api-client==1.5.8` can't be installed because it
doesn't have a source distribution or wheel for the current platform
which is the honest state of the dependency, and better than silently running
against a client that cannot satisfy the tool. To work locally before the
release, install the client from source and skip the re-sync:
uv pip install --python .venv/bin/python ../hummingbot-api-client
uv run --no-sync python -m pytest tests/
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mt84XBEMVxbbyMG8fDxDKj
Rebasing onto #203 moves this branch onto a base that enforces `black --check .` and `isort --check .` in CI, which main did not. The code was written to match the surrounding pre-reformat style, so it needs a pass with the locked tools (black 26.1.0, isort 8.0.1). Formatting only — no behaviour change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mt84XBEMVxbbyMG8fDxDKj
Last agent still on `claude-acp:opus`; every other claude-acp agent already runs sonnet, so this makes the default uniform across the agent set. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mt84XBEMVxbbyMG8fDxDKj
- Agent identity is now options-first: the Derive options positioning read (25D risk reversal, put/call OI, IV term structure, net GEX) is the core signal, with the options_flow routine and Derive public API reference documented at agent level. - The smart-money capital-flow content (cross-market regime + Solana on-chain pulse) moves out of AGENT.md into a renamed smart_money_flow strategy (was derive_flow_trader), which now also reads options_flow each tick as a confirmation/sizing input and tie-breaker. - Add options_flow routine and options_oracle_operator strategy; set both strategies to a 5-minute cadence and agent_key claude-acp:sonnet. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- `options_flow.py` failed `black --check` and `isort --check`, which #203's CI enforces repo-wide. Formatting only. - `options_flow.py` line 433 wrote a Markdown underscore escape as `\_` inside a non-raw string. Python does not recognise `\_`, so it kept the two characters but raised SyntaxWarning (an error in a future version). Doubled to `\\_`: identical rendered output, no warning. The GeckoTerminal rate-gate guard needed no fix here. #202 only *renamed* `onchain_flow.py`; #203 had already rewritten that file to call `pool_data.gecko_request`, so the merge took the rename with the rate-gated content and `test_nobody_hand_builds_a_geckoterminal_url` passes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mt84XBEMVxbbyMG8fDxDKj
…redicate build_mcp_servers_for_session now holds every server candidate to has_server_access(user_id, name) before its credentials go into the subprocess env. The _NumericPasswordServer stub predates that check, so the test died on AttributeError instead of exercising the str-coercion it pins. Grant the stub unconditional access — the test's subject is credential stringification, not authorization. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0166iQoxKce23GkUwuQJxdkr
fengtality
force-pushed
the
feat/lp-close-retry-ownership
branch
from
August 18, 2026 18:41
02684d5 to
3d9f2ef
Compare
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.


Summary
Condor-side of the gateway#678 retry-ownership work, plus the CLMM
bin_countwiring, and it absorbs the Derive Options Trader agent rename from the now-closed #202 (see below). Canonical design:docs/retry-architecture.md(in the gateway PR). Agents drive executors throughmanage_executors, where no controller exists to react to a stranded position — and the tick-prompt summary was RUNNING-only, so a terminal executor still holding a live on-chain position was invisible to the agent.Orphaned positions
POSITION_HOLDwithhold_reason, the injectedorphaned_positionflag, or legacyFAILED-with-position) as a reason-aware 🚨 ORPHANED POSITION line with explicit recovery guidance — a freshlp_executorcannot adopt an existing position and would mint a second one. Also exposed asorphaned_executorsin provider data.manage_executors: neworphaned(list recovery candidates) andresolve_orphan(mark recovered) actions wired to the new API endpoints.resolve_orphanwithoutexecutor_idnow returns a required-input error pointing ataction="orphaned". It previously fell throughget_flow_stage()toshow_schema/list_types, so an agent's recovery request was silently answered with executor-type listings.already_terminatedpayload (close_type, position address, orphan warning) is surfaced in both the MCP tool result and the Telegram stop menu instead of being treated as a plain stop.guides/lp_executor.md: close-exhaustion terminates as an involuntaryPOSITION_HOLD(hold_reason: close_retries_exhausted);FAILEDmeans nothing-left-on-chain; orphan recovery flow documented, including that thelp_rebalancerhalt is in-memory and clears on controller restart — so restart is the acknowledgment step after resolving.manage_clmm— the tool that closes an orphanThe lifecycle above could flag an orphan but not clear it. Every warning said to close the position "via the gateway tools" — an instruction with no implementing tool. An agent following it found that
manage_executors(action="stop")is a no-op (the executor has already terminated, which is the correct contract),manage_ammhandles AMMs only, andexplore_dex_poolsis read-only. The position stayed open.manage_clmmtool, mirroringmanage_amm: progressive-disclosure guide, per-action validation, dispatch toclient.gateway_clmm.*. Actions:position_info,open,add_liquidity,remove_liquidity,close,collect_fees. Pool discovery stays inexplore_dex_poolsrather than being duplicated.lp_provider: "orca/clmm"while Gateway routes on the bare"orca", so the connector is normalised; and anlp_executorposition is absent from the API database, soclosemust forwardpool_addressor the API returns400.connector_nameholds the network, not the DEX, which is the easiest way to hand-build a dead call. Records awaiting reconciliation emit no call, so a close is never suggested with an unknown position address.manage_executorsdocstring, orphan listing) now namemanage_clmmand state that stopping will not close the position.CLMM
bin_countmanage_gateway_clmm(action="get_pool_info", bin_count=N)requests the per-tick liquidity distribution around the active price. Meteora always returns its bins; orca, raydium, uniswap and pancakeswap compute them on request, so the default of0keeps pool-info cheap. Requireshummingbot-api-client1.5.8 (pinned here) for the typed parameter.Absorbs #202 (Derive Options Trader)
#202 renamed the
smart_money_flowagent toderive_options_trader, added theoptions_flowroutine andoptions_oracle_operatorstrategy, and moved both strategies off the opencode custom endpoint ontoclaude-acp:sonnet. It was branched frommainbefore #203, so it missed two gates #203 adds — and its own CI stayed green because those gates don't exist on its base. Merging it here rather than after would have turnedmainred on merge.test_nobody_hand_builds_a_geckoterminal_url, which fails any source containingapi.geckoterminal.com. feat(agents): rename Smart-Money Flow agent to Derive Options Trader #202 carriedmain's pre-fixonchain_flow.py, which set its own base URL and opened a barehttpx.AsyncClient, bypassing the shared per-IP budget owned bycondor/pool_data.py. Simulating the merge reproduced the failure. No code change was needed in the end: feat(agents): rename Smart-Money Flow agent to Derive Options Trader #202 only renamed that file, and Feat/trade panel dex #203 had already rewritten it to callpool_data.gecko_request, so the merge kept the rename with the rate-gated body. Verified byte-identical tomain's copy first, so nothing of feat(agents): rename Smart-Money Flow agent to Derive Options Trader #202's was dropped.options_flow.pyfailedblack --checkandisort --check, which Feat/trade panel dex #203 enforces repo-wide. Reformatted.options_flow.py:433wrote a Markdown underscore escape as\_inside a non-raw string. Python does not recognise\_, so it kept both characters but raisedSyntaxWarning— an error in a future Python. Doubled to\\_; rendered output unchanged.#202 is closed with a comment pointing here.
Dependency: requires the unreleased client 1.5.8
pyproject.tomlrequired==1.5.8whileuv.lockstill resolved 1.5.6, so everyuv run --frozensilently re-synced the venv back down. That downgrade is not cosmetic — 1.5.6 has nopool_addressonclose_position, somanage_clmm's close died with:The tool's unit tests stub the client object, so they passed throughout; only a live call surfaced it. The lock now names 1.5.8. Its sdist/wheel hashes are absent because 1.5.8 is not on PyPI yet — a plain
uv lockfills them in once hummingbot/hummingbot-api-client#25 merges and releases, anduv lock --checkaccepts the entry as consistent withpyproject.tomlmeanwhile.Until that release
uv run --frozenfails loudly (can't be installed because it doesn't have a source distribution or wheel), which is the honest state of the dependency and strictly better than silently running against a client that cannot satisfy the tool. To work locally before the release:This PR merges last — after #203, then after hummingbot/hummingbot-api-client#25 is released.
Companion PRs
docs/retry-architecture.md(in the gateway PR)binCounton unified CLMM pool-infoPOSITION_HOLD, fresh position reads, bounded pending-tx pollingbin_countpassthrough, Raydium routed through Gatewaybin_countonget_pool_info(1.5.8)bin_countonget_pool_infoBased on #203
This PR targets
feat/trade-panel-dex(#203), notmain, since #203 merges first. It has been rebased onto that branch, so the diff here is only this branch's own commits (16 files) rather than 330.Conflicts resolved during the rebase:
tools/gateway_clmm.py— Feat/trade panel dex #203's plainget_pool_infocall vs. this branch'sbin_count. Resolved to the typedbin_count=call in Feat/trade panel dex #203's formatting; the intermediate_getpassthrough commit is superseded later in the series.formatters/__init__.py— Feat/trade panel dex #203 reformatted imports and moved the gateway block below executors;format_clmm_resultadded to the relocated block.schemas.py— Feat/trade panel dex #203 reflowed theAMMRequesttail to multi-line; kept their formatting and appendedCLMMRequestafter it.uv.lock— Feat/trade panel dex #203 bumps the client to the published 1.5.7, this branch needs 1.5.8 (see below); resolved to 1.5.8.#203 also adds a CI gate (
black --check .,isort --check .) thatmaindoes not have, so a formatting-only commit runs the locked tools (black 26.1.0, isort 8.0.1) over this branch's files.The liquidity depth column in #203 shows bins only for Meteora until
fetch_liquidity_binspassesbin_count— details and the one-line change are in a comment on that PR.Merge order
Until step 2,
uv synchere cannot install (hummingbot-api-client==1.5.8is not yet on PyPI), so CI will be red at the dependency step. Lint/test failures inherited from the base branch are being addressed in #203; all 12 Python files in this PR passblackandisortunder the locked versions.Validation
2268 tests pass on the rebased branch (
uv run --no-syncagainst a locally built 1.5.8), including 24 coveringmanage_clmmdispatch/validation and the exact recovery call the orphan listing emits, plus theresolve_orphanrequired-input contract. One unrelated test,test_agents.py::test_numeric_credentials_reach_the_subprocess_as_strings, fails identically on the barefeat/trade-panel-dexbase. Validated live on mainnet with the companion branches deployed: forced close-failure cascade → 🚨 orphan surfaced in the tick prompt →already_terminatedon re-stop → recovery →resolve_orphancleared it.bin_count=4returns populated bins for orca and raydium through the MCP tool;bin_count=0returns none.🤖 Generated with Claude Code
https://claude.ai/code/session_01Mt84XBEMVxbbyMG8fDxDKj