Repository navigation
Description
Audit: 2026-05-21 multi-reviewer SDK sweep
This umbrella tracks the 34 issues opened from a parallel-reviewer audit of v2.3.0 spanning security, HTTP transport + concurrency, WebSocket subsystem, models + types + Decimal, resource API surface, performance hot paths, test coverage + contract drift, and docs + DX.
Goal: harden the SDK against the workloads it markets to — real-time orderbook streaming, automated trading, integration into production trading systems.
Reviewer outputs are captured in this orchestrator's agent:// artifacts (one per reviewer). All evidence is cross-referenced inline in each child issue with file:line + verbatim code excerpts.
Tier 1 — Critical / High (release-blocking class)
Correctness or money-impact defects on the hot paths. These should land before the next minor release.
- WS: orderbook permanently desyncs after a sequence gap (no resnapshot) #189 — [critical] WS: orderbook permanently desyncs after a sequence gap (no resnapshot)
- Page.to_dataframe / Page.to_polars silently corrupt Decimal columns (str instead of Decimal) #190 — [critical]
Page.to_dataframe/to_polarssilently corrupt Decimal columns - DollarDecimal serialization can emit scientific notation (Kalshi may reject) #191 — DollarDecimal serialization can emit scientific notation
- Retry: include Cloudflare 5xx (520–524) and 408 in RETRYABLE_STATUS_CODES #192 — Retry: include Cloudflare 5xx (520–524) and 408 in
RETRYABLE_STATUS_CODES - Retry: cap total wall-clock budget; no per-request deadline today (~180s worst case) #193 — Retry: cap total wall-clock budget (no per-request deadline today; ~180s worst case)
- Orders: V1 batch_create / batch_cancel must return typed per-leg responses (partial-success defect) #194 — Orders: V1
batch_create/batch_cancelmust return typed per-leg responses (breaking) - WS: generic subscribe() silently drops unknown params keys #195 — WS: generic
subscribe()silently drops unknown params keys - WS: server-side seq reset silently dispatches subsequent messages (no gap fired) #196 — WS: server-side seq reset silently dispatches subsequent messages
- WS: reconnect should fast-fail on permanent close codes (1008, 4001…) #197 — WS: reconnect should fast-fail on permanent close codes (1008, 4001…)
- WS: count/size/timestamp payload fields typed as raw str (should be Decimal/datetime) #198 — WS: count/size/timestamp payload fields typed as raw
str(should be Decimal/datetime) - WS: orderbook snapshot rebuilt and discarded on every frame (hot-path waste) #199 — WS: orderbook snapshot rebuilt and discarded on every frame (perf hot path)
- Docs: migration guide missing v2.1→v2.3 sections + self-contradicting list_all() cap #200 — Docs: migration guide missing v2.1→v2.3 + self-contradicting
list_all()cap
Tier 2 — Medium (next minor)
Important correctness, consistency, and DX gaps.
- Error mapping: 422 / 409 / 408 / 410 fall through to bare KalshiError (+ tests for fallback) #201 — Error mapping: 422/409/408/410 fall through to bare
KalshiError(+ tests) - Config: validate base_url includes /trade-api/v2 path component (silent sign-path corruption) #202 — Config: validate
base_urlincludes/trade-api/v2path component - Errors: cap _map_error response body buffering (memory + log volume) #203 — Errors: cap
_map_errorbody buffering (memory + log volume) - Errors: add KalshiTimeoutError + KalshiPoolExhaustedError (typed timeout semantics) #204 — Errors: add
KalshiTimeoutError+KalshiPoolExhaustedError - WS: order_group_updates sequence gap has no recovery path (consumer silently misses events) #205 — WS:
order_group_updatessequence gap has no recovery path - WS: unsubscribe leaks orderbook state (long-running sub/unsub cycles grow memory) #206 — WS: unsubscribe leaks orderbook state
- WS: ERROR backpressure strategy silently terminates consumer iterator (no programmatic signal) #207 — WS: ERROR backpressure strategy silently terminates iterator
- WS: expose ws_ping_interval / ws_close_timeout via KalshiConfig (currently hardcoded) #208 — WS: expose
ws_ping_interval/ws_close_timeoutviaKalshiConfig - WS: pluggable JSON loader/dumper hook (orjson opt-in) for recv/send hot path #209 — WS: pluggable JSON loader/dumper (orjson opt-in) for hot path
- Auth: KalshiClient.close() shouldn't shut down externally-provided KalshiAuth executor #210 — Auth:
KalshiClient.close()shouldn't shut externally-providedKalshiAuthexecutor - Resources: URL-encode user-supplied path segments (urllib.parse.quote) #211 — Resources: URL-encode user-supplied path segments
- Resources: validate "limit" ranges client-side per spec bounds #214 — Resources: validate
limitranges client-side per spec bounds - Errors: KalshiSequenceGapError + KalshiSubscriptionError lack channel / sid / seq context #213 — Errors:
KalshiSequenceGapError+KalshiSubscriptionErrorlack channel/sid context - Errors: AuthRequiredError message omits KALSHI_PRIVATE_KEY_PATH #215 — Errors:
AuthRequiredErrormessage omitsKALSHI_PRIVATE_KEY_PATH - Types: _coerce_decimal silently accepts bool → cryptic InvalidOperation #216 — Types:
_coerce_decimalsilently accepts bool → cryptic InvalidOperation - Auth: support passphrase-protected PEMs (currently forces plaintext key on disk) #217 — Auth: optional passphrase-protected PEM support
- Docs: README/websockets.md disagree on WS channel count + README method names are wrong #218 — Docs: README/websockets.md disagree on WS channel count + wrong method names
- Testing: parametrize phantom-kwarg behavioral test across all Request models #219 — Testing: parametrize phantom-kwarg behavioral test across all Request models
Tier 3 — Polish bundles (opportunistic)
Each is a small atomic refactor; bundled to keep the issue tracker focused.
- Polish bundle: HTTP transport DX (http2 extra, per-request headers, Retry-After date form, trailing-slash signing, RecordingTransport headers, extra_headers docs) #220 — Polish bundle: HTTP transport DX (http2 extra, per-request headers, Retry-After HTTP-date, trailing-slash signing, RecordingTransport headers, extra_headers docs)
- Polish bundle: WS small items (Full Jitter reconnect, side Literal, populate_by_name, AwareDatetime, sentinel idempotence) #221 — Polish bundle: WS small items (Full Jitter reconnect, side Literal, populate_by_name, AwareDatetime, sentinel idempotence)
- Polish bundle: docs cleanup (env-vars NameError, stale AUDIT doc, kalshi_python_async ref, limits default, cancel idempotency docs, WS perf section, events.get default) #222 — Polish bundle: docs cleanup (env-vars NameError, stale AUDIT doc, kalshi_python_async ref, limits default, cancel idempotency docs, WS perf section, events.get default)
- Polish bundle: perf + testing (cursor guard O(1), model_dump_json bytes, bench scripts, from_env precedence test, integration env isolation, KalshiAuth.close terminality) #223 — Polish bundle: perf + testing (cursor guard O(1),
model_dump_jsonbytes, bench scripts,from_envprecedence test, integration env isolation,KalshiAuth.closeterminality)
Suggested release plan
- Patch (v2.3.1) — WS: orderbook permanently desyncs after a sequence gap (no resnapshot) #189 (orderbook resync) + Page.to_dataframe / Page.to_polars silently corrupt Decimal columns (str instead of Decimal) #190 (DataFrame Decimal) + DollarDecimal serialization can emit scientific notation (Kalshi may reject) #191 (scientific notation). These three are user-money-impact and have small, non-breaking fixes.
- Minor (v2.4.0) — bulk of Tier 1 (Retry: include Cloudflare 5xx (520–524) and 408 in RETRYABLE_STATUS_CODES #192, Retry: cap total wall-clock budget; no per-request deadline today (~180s worst case) #193, WS: generic subscribe() silently drops unknown params keys #195–Docs: migration guide missing v2.1→v2.3 sections + self-contradicting list_all() cap #200) + non-breaking Tier 2 (most of M*/D*).
- Major (v3.0.0) — Orders: V1 batch_create / batch_cancel must return typed per-leg responses (partial-success defect) #194 (V1 batch endpoints, breaking) + any opportunistic Tier 2 with breaking shape.
- Polish bundles — fold into v2.4 / v3.0 wherever a bundle item naturally co-occurs with a tracked issue.
Source
Identified during the 2026-05-21 parallel-reviewer audit (8 subagents: security/auth, HTTP transport + concurrency, WebSocket, models + types, resources, performance hot paths, tests + contract drift, docs + DX).
Activity
TexasCoding commentedon May 21, 2026
Release-cut decision: fold #194 into v2.4.0
Per audit-orchestrator discussion with maintainer (2026-05-21): the V1
batch_create/batch_canceltyped-response fix lands in v2.4.0 minor rather than waiting for v3.0.0. Rationale: the current V1 implementation is unsafe as-shipped (crashes on partial-success, discardsreduced_by_fp), the V2 family already exists as the safe path, and breaking the V1 return type is the right correctness call.Revised release plan:
This is the de-facto active milestone now.
docs(roadmap): point Open trackers at #224 audit umbrella
feat(errors): add Conflict/Timeout/PoolExhausted; structure WS error …
docs: add v2.1→v2.3 migration sections; fix list_all cap claim; align…
fix(types): preserve Decimal in DataFrames; positional serialization;…
feat(transport): widen retry codes; typed timeouts; bounded errors; b…
feat(resources): V1 batch typed responses (breaking); URL-encode path…
fix(ws/models): type count/size as FixedPointCount; timestamps as dat…
fix(ws): orderbook resync; seq reset detection; permanent close codes…
fix(ws): orderbook resync; seq reset detection; permanent close codes…
perf(ws): inplace orderbook updates; config knobs; pluggable JSON; sm…
docs+polish: env-vars NameError, stale AUDIT/predecessor refs, limits…
feat(transport,auth): http2 extra, per-request headers, Retry-After d…
docs+polish: env-vars NameError, stale AUDIT/predecessor refs, limits…
feat(transport,auth): http2 extra, per-request headers, Retry-After d…
test(models): parametrize phantom-kwarg behavioral test across all Re…
test(models): parametrize phantom-kwarg behavioral test across all Re…
polish: cursor guard O(1); batch bytes path; bench scripts; close ter…