piker/ai/ib-recovery-candidate.md

234 lines
11 KiB
Markdown
Raw Permalink Normal View History

# Bounded IB recovery candidate
Prepared 2026-10-22 on `wkt/land_ib_recovery`, in the main checkout
`/home/goodboy/repos/piker`. Base:
`dc618229bd8c9c90aa3160e26677b553a7d472c7`.
Changes are unstaged and uncommitted. No acceptance is implied.
## Patch selection and provenance
Keep these existing landing commits:
| Commit | Reason |
| --- | --- |
| `4133f073` and its ancestors | Packaging, Nix, runtime API and pin work |
| `e8e0730a` | Correlate MethodProxy requests and responses |
| `7bef50b0` | One response reader, per-request waiters |
| `d1571ddf` | Propagate daemon-handler exceptions |
| `dc618229` | Isolate cached feed stream consumers |
Proposed separate implementation boundaries, in this order:
1. Back out the code and original test from
`635314c757c74a1cd95c5cf3dc7dfd03be984b00`.
This restores the direct-owner `_quote_streams` implementation.
Retain the historical prompt records. Replace the removed
mocked-cache regression with the real-bridge restart reproducer.
This backout is not a claim that arbitrary concurrent direct
callers have safe broadcast ownership.
2. Backport **only** `piker/brokers/ib/feed.py` from
`0846cbd4a4197e47415689cf5e9d146ba3174c32`.
Retain its seven history-adapter regressions in
`tests/test_ib_history.py`. This includes reset serialization,
completion/result signalling and bounded cancellation/failure.
It does not include the original SHM/storage orchestration patch.
3. Backport **only** `piker/brokers/ib/feed.py` from
`1da9b760a3bc6b96501110cab2b5d09846d42a1d`.
Retain `test_empty_history_preserves_request_boundary` and its
six cases. This preserves no-data request boundaries and wakes
the waiter. It does not include the original history-layer
endpoint/filtering changes. Add the real-bridge history recovery
case after these adapter changes are present.
4. Import the code and original regression from
`12b0d66e854247e92cb5a8668be71d816c72d856`.
Add a separate real-broadcaster/production-loop regression.
5. Remove the single-null-sample debugger stop in `get_null_segs()`.
This is a local UX cleanup retaining the existing `None` result.
It does not backport the null-grouping/deduplication rewrite in
`976db6c33c4b8ed07eee3047226417d3928d9cca`.
6. Record cross-boundary regression results and live qualification
evidence, distinguishing the known callback failure from passes.
All three imported source commits were authored by
`goodboy <goodboy_foss@protonmail.com>`; preserve that authorship
and the full source SHA when committing each partial backport.
Use explicit partial-backport wording, not a whole-commit cherry
claim. New real-runtime tests are this session's additional work.
The current aggregate working diff contains these boundaries in
the same feed file. Staging that entire file would combine the
first three boundaries. Reconstruct/stage the original per-source
patches in the above order when preparing commits.
## Quote-cache history correction
`4af885d90f6e4bcfa963f2f7a1d40436d3c928f3` and landing commit
`635314c757c74a1cd95c5cf3dc7dfd03be984b00` have the same stable
patch ID: `bc54892c25e957be112b135bb9e383ec4dd123c5`.
They are equivalent replays, not different implementations.
`wkt/ib_downstream_pin_eval@e929fdc2` lacks that quote-cache
change and retains direct ownership. The original qualification
did not validate the combination assembled for the landing.
After this candidate's edits, `piker/brokers/ib/feed.py` matches
the source at `1da9b760` byte-for-byte. Compared with the evaluated
tip, only the later schedule/reservation APIs are absent.
The EMS code patch has the same stable patch ID as the original
`12b0d66e` EMS code patch:
`50fee047e94fd67c8d9feab66859fbe3ab25b64c`.
## Real-runtime evidence
`tests/test_ib_quote_restart.py` uses real TCP Tractor actors,
an asyncio-infected child, the installed pinned Tractor bridge,
production Piker quote setup, and real IB/eventkit ticker events.
Only the external IB connection/input is replaced with replay data.
Before the quote-cache backout, cancellation after ticker delivery
raised `RemoteActorError[TrioTaskExited]` through
`maybe_open_context()` / `_Cache.run_ctx` teardown, matching the
reported fatal path.
After the backout:
- Two quote generations finish; each subscription is cancelled,
each asyncio producer is done and each ticker handler removed.
- A second RPC repeats the sequence in the same actor successfully.
- The strict callback-cleanliness case still fails: Tractor's
`signal_trio_when_done()` raises `TrioTaskExited` into asyncio's
exception handler even though the Trio consumer recovers.
The history integration case keeps `Client.bars()`, MethodProxy,
both relays, `get_bars()`, `wait_on_data_reset()` and
`data_reset_hack()` real. It replaces external contract discovery,
IB history replies and VNC input. A real IB error event announces
farm recovery. The replay rejects retries before recovery and
proves one reset followed by completion for both 1s and 60s data.
The new EMS case runs the production dark-clearing loop over a
real Tractor broadcaster: both EMS and a sibling receive a tick,
EMS cancellation removes only its subscription, and the sibling
continues with an unchanged strict lag policy.
## Verification
Unique selected cases: **38 passed, 1 failed**.
The failure is intentionally visible, not skipped or xfailed.
Run from the checkout's working Nix environment:
```xsh
timeout -k 5 45 py313/bin/python -m pytest -p no:xonsh -q -x --tb=short tests/test_ib_history.py tests/test_ems_broadcast.py tests/test_ib_method_proxy.py tests/test_feed_broadcast.py
timeout -k 5 45 py313/bin/python -m pytest -p no:xonsh -q --tb=short tests/test_ib_quote_restart.py
timeout -k 5 45 py313/bin/python -m pytest -p no:xonsh -q -x --tb=short tests/test_logging.py tests/test_startup.py
timeout -k 5 45 py313/bin/python -m pytest -p no:xonsh -q -x --tb=short tests/test_services.py::test_runtime_boot
git diff --check
```
The adapter/proxy/feed/EMS selection initially passed 21 cases;
the added real EMS case then passed with its imported case.
The bridge file passed two cases and failed callback cleanliness.
Logging/startup passed 13 cases; runtime boot passed one with the
existing `Registrar.uid` deprecation warning.
The agent shell inherited another project's environment and lacked
native libraries. Tests used the existing Piker `py313` via
`/tmp/nix-shell.zfKbeB/opencode/piker_test_env.py`, which supplies
libraries from the pinned flake's existing Qt dependency closure,
excluding Nix Qt itself and glibc. No environment sync or shellHook
ran. Piker resolved to this checkout; Tractor resolved to its
installed package in `py313`.
Workflow-state preparation initially reported changed legacy
files/modes after migration. On 2026-10-23 the human authorized
reconciliation. Inspection found only two missing legacy guidance
snapshots; the neutral copies matched their saved hashes and modes.
Restoring those local snapshots resolved preparation without
changing archived messages or the original migration receipt.
## Human live qualification, 2026-10-23
The human ran these journeys with the installed Tractor pin,
the existing IB Gateway and pre-existing NativeDB. The agent did
not operate the broker connection, VNC, orders or database.
- Initial QQQ startup/backfill recovered after pacing error 162
with one reset attempt, then displayed live updates. The reset
waiter appears to have timed out; the log did not establish an
acknowledged farm reset or live quote-stream restart.
- Sequential chart exits and reattachment preserved all four
daemon PIDs; a read-only process/listener check confirmed this.
- The overlapping two-chart journey used `pikerd@961108`,
`brokerd.ib@964765`, `samplerd@965900` and `emsd@969015`.
B started at 08:16:31; C at 08:17:33. B closed at 08:18:11,
replacement B started at 08:18:24 and had FSPs up by 08:18:28,
then C closed at 08:18:37. The human reported the exercise worked;
survivor updates are a manual observation, while timestamps and
unchanged daemon identities corroborate overlap and service reuse.
- Startup in that journey hit the explicit single-null-sample
breakpoint several times before the charts overlapped. The small
debugger cleanup above was applied afterward; this live run does
not itself verify that later edit.
- Departing charts still report cancellation groups and an FSP
receiver-close `ClosedResourceError`, followed by the Qt close
handler's self-SIGINT. Peer-disconnected final result warnings
remain contained in the persistent services.
- Daemon SIGINT at 08:19:43 reproduced cached shared-client teardown:
`load_clients_for_trio()` / `_Cache.run_ctx` promoted
`TrioTaskExited` through `open_trade_dialog()` and entered PDB.
After the human continued, all three children were reported
exited with status 0 and the root terminated at 08:19:45.
Cancellation-only RPC error reporting and late cancel-ack
diagnostics also remain.
The human explicitly accepted deferring this daemon teardown fix
from #94 and requested loud attribution for the bounded backports.
This is an operational smoke result, not clean-shutdown coverage.
## Downstream reconciliation requirements
Before landing #94, the human wants an easy replay of the downstream
histories intended to follow it. Rehearse against the agreed #94
tree in disposable worktrees, then inspect range-diffs, final code
and relevant regressions. This note does not authorize rewriting
the original branches or changing acceptance markers.
- `0846cbd4` and `1da9b760` are partial backports. Their imported
adapter hunks may already be present, but their history/SHM/storage
changes and omitted integration tests remain necessary downstream.
Never drop either whole source commit merely because `feed.py`
matches or its adapter hunk becomes empty.
- `12b0d66e` has source-equivalent EMS code and its imported test,
plus an additional local real-broadcaster test. Preserve that extra
regression when resolving test-file overlap.
- The quote-cache backout intentionally restores direct ownership
for this landing. Reintroducing original `4af885d9` downstream
requires qualifying cached-owner teardown with the matching
Tractor revision; the backout is not a permanent ownership design.
- The local single-null breakpoint removal retains legacy behavior.
Prefer the full downstream `976db6c3` algorithm when it is replayed;
preserving this `return None` guard would lose that later fix.
- Real-bridge tests and manual qualification notes are local work,
not evidence that omitted portions of the source commits landed.
Commit messages must name full source SHAs, original authorship,
imported paths/tests, omissions, local additions and replay guidance.
Use explicit partial-backport language for adapter subsets; do not
claim a full cherry-pick or reuse the source's broader fix claims.
## Remaining decision
This candidate removes the reproduced fatal quote-restart
propagation and imports existing reset fixes. The human exercised
live QQQ startup, backfill and concurrent chart consumer lifetimes.
Completion-callback and teardown errors remain; the human accepts
them as follow-ups outside the bounded #94 landing.
The storage/SHM portions of the downstream fixes are not covered by
the adapter backport or its integration tests.
`ddac3553` is Deribit-specific and omitted. Qt lifecycle commit
`1a2d4893` stays a separate follow-up. Further Tractor teardown
changes or storage imports require reassessing the bounded landing
rather than silently expanding this candidate.