Repository navigation
Recovery exits each held symbol on its own fresh quote (#215 part 1) - #486
Merged
seathatflowsinourveins merged 2 commits intoSep 29, 2026
Merged
Conversation
recovery.recover took the alphabetically first held symbol and ended with recovery_quote_not_fresh after one quote_max_age wait, so one held symbol without a fresh quote ended recovery before any symbol after it was sold. Each exit now takes the alphabetically first held symbol whose own quote is fresh at that turn (not the freshest). A symbol without one is skipped and exited once it quotes. No exit starts, for a symbol quoted all along as for one that has just quoted, unless a whole exit (two order timeouts: the fill wait and a cancel's wait) still fits before the order deadline. The check runs after every wait, so bounded() cannot cut a late exit after its POST. When no whole exit fits, recovery ends with recovery_quote_not_fresh if a held symbol is still unquoted (named in the result's new stale_symbols field) and otherwise with recovery_deadline_reached. No exit is priced without its own fresh quote, and a failed exit, including the transport's pre-wire not_sent refusal, still ends recovery. mover_runner.py: the forced recovery's port and the recover command's port require only the benchmark quotes, as a trial's port does, so a sparse held mover cannot keep the recovery transport from starting. Readiness then no longer shows that the port subscribes every held symbol, so recover_mover raises held_symbol_not_subscribed before recovery starts the port when a held or unresolved symbol is not subscribed. Tests, written first: - PerSymbolRecoveryExits (5 tests): 4 fail on d063623 and pass now. test_no_exit_starts_once_a_whole_exit_no_longer_fits (the first exit fills after the last point a whole exit fits while the other symbol freshens during that fill wait) also failed on this change's first revision, which started the second exit (['SRZN', 'PFSA'] != ['SRZN']). - RecoveryPortSubscription (test_adaptive_paper_mover.py): fails on d063623 and on the first revision (SafetyError not raised), passes now. - MoverPaperCommandWiring.test_recovery_ports_require_only_the_benchmark_quotes: failed on d063623 (required baskets SPY, SPY, BBB, BBB), passes now. - Three AsyncTransport guards (an admission freeze does not block an owned exit with a fresh quote; an exit without its own fresh quote is refused before the wire as not_sent; start() waits on the required basket only) pass before and after; transport.py is unchanged. README-recovery.md scopes the per-symbol handling to the mover's recovery ports: runner.py's recovery port (the adaptive lane) still gates start() on every held symbol's quote until the F1 follow-up. It qualifies the exit order (alphabetical, not the freshest; a pre-wire refusal still ends recovery), states that stale_symbols is meaningful only together with recovery_quote_not_fresh, and its Verification section covers the PerSymbolRecoveryExits cases without stale test counts. README-mover.md describes the new exit order, the whole-exit stop and the subscription guard. source-hashes.json and manifests/evidence.json are re-registered per the docs/lanes.md hot-file protocol on origin/main; the component matrix and the new-host grand list regenerate unchanged. runner.py, transport.py, safety.py, native_adapter.py and order_contract.py are unchanged. #215 stays open for part 2, the ambiguous GRML intent and the trial C loop stall. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ce and timing tests (#486 GPT-6 review) Implemented by GPT-6 (gpt-6-astra, effort max) through codex exec in the owned worktree, test-first; acceptance re-run by the coordinator before commit. Co-Authored-By: GPT-6 (Codex) <noreply@openai.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
seathatflowsinourveins
force-pushed
the
claude/recovery-per-symbol-quote-215-20260929
branch
from
September 29, 2026 04:10
9d6b873 to
88536ec
Compare
seathatflowsinourveins
enabled auto-merge (squash)
September 29, 2026 04:10
Owner
Author
|
Cross-family review round. GPT-6 (
Coordinator acceptance on the pre-rebase tree:
That completes one review and one repair per reviewer family. Guarded auto-merge is set at |
seathatflowsinourveins
deleted the
claude/recovery-per-symbol-quote-215-20260929
branch
September 29, 2026 04:52
This was referenced Sep 29, 2026
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
This fixes issue #215, part 1. Before this change, one stale or sparse held symbol could stop recovery from exiting any other symbol. It does not close #215: part 2 (the ambiguous GRML intent with no broker record) and the trial C run-loop stall stay open.
recovery.py: each exit gates on its own symbol's quote.order_timeout) still fits before the deadline. Otherwise it ends withrecovery_quote_not_freshif a held symbol is still unquoted, elserecovery_deadline_reached.stale_symbols, which is meaningful only together withrecovery_quote_not_fresh.mover_runner.py: the recovery ports' readiness waits on the benchmark quotes only, as the trial port's already does.command_paperand the port incommand_recover.recover_moverfails closed withheld_symbol_not_subscribedunless every held and unresolved symbol is subscribed.transport.py,runner.py,safety.py,native_adapter.pyandorder_contract.py, so the native-fault gate's bound sources do not move.runner.py's recovery port still gatesstart()on every held symbol, until the F1 follow-up.README-recovery.mdstates this.Tests (failing-first)
PerSymbolRecoveryExits(5 cases): a stale symbol doesn't hold back a fresh one; a stale symbol is exited once it quotes; all-stale waits to the deadline and sends nothing; a late quote starts no exit; no exit starts once a whole exit no longer fitsMoverPaperCommandWiring.test_recovery_ports_require_only_the_benchmark_quotes[['SPY'],['SPY'],['BBB'],['BBB']] != [['SPY']]*4RecoveryPortSubscription.test_a_held_or_unresolved_symbol_the_port_does_not_subscribe_fails_closedSafetyError not raisedChecks at
9d6b873b(rebased on mainb0fb65b4)evidence_manifest --checkpassed;validate.pypassed.trading_gates.py --checkpassed: native-fault-behaviour stays at bound 6, stale 3 (runner, safety, transport), unchanged from base.git diff --checkclean; the three registry tests OK.evidence-reviewerreturned changes-needed on the first revision (1 must-fix, 1 should-fix, 4 nits), and all were repaired;stack-verifierreturned pass twice.Evidence class
Local synthetic tests with fake ports and streams only. No broker run, and no paper acceptance for this path. The mover recovery start now depends on a SPY quote within
start_timeout(15 s), which is unmeasured on the broker after hours.SOTA sources
cc4cb3b7,transport.pySDK_COMMIT),alpaca/data/live/websocket.py:data_timeoutis off by default for legitimately quiet subscriptions;crates/risk/src/engine/mod.rs:native_adapter.pyis the adapter.🤖 Generated with Claude Code