Repository navigation
Rename the Kalshi sports row's category to events - #152
Conversation
juan-malbeclabs
left a comment
There was a problem hiding this comment.
One operational gap worth fixing before this is called done; the rename itself is clean and carefully scoped.
The rename does not reach a default deployment
Dockerfile:107 bakes ENV DZ_FEED_REGISTRY_URL=https://get.doublezero.xyz/feeds/doublezero-edge-feeds-latest.json, and Origin::from_flags gives a URL document precedence over the compiled-in copy (falling back to it only when the fetch or validation fails). So on a normal container src/ingest/registry.json is the fallback, not the authority — and the hosted document served right now still says:
KALSHI | perps | edge-kalshi-perps-tob
KALSHI | perps | edge-kalshi-perps-mbp
KALSHI | sports | edge-kalshi-sports-mbp
Two consequences:
- Until that document is republished with
"category": "events", the rename is inert wherever the URL resolves./v1/status,/admin/channelsand the(category: …)suffix on/v1/productskeep reportingsports, and the arbitration scope, tape-owner rank and catalog/book/history keys keep being built onsports. The changelog promises a change an operator will not see. - Independently of this PR: that hosted document is already well behind the built-in — it carries neither the
electionsrows nor the Phoenix row. This is the same trap the### Fixedentry about the stalelashay-*codes called out ("the hosted feed-registry document must carry the same three codes"), and it is evidently still open.
Neither is a code change here. But the changelog entry should say the hosted document must be republished for the rename to take effect, the way the lashay-* entry did — otherwise the next reader concludes the rename shipped.
Verified, no action needed
- No state migration.
categoryis part ofhistory::Key, the catalog key andauthority::MarketKey, so a rename could in principle orphan persisted state.src/history.rstouches no filesystem — the store is in-memory and rebuilt per process, so the rename costs nothing across a restart. - Not on the wire.
categoryis producer-side only;PROTOCOL.mdcorrectly untouched. --channels/DZ_CHANNELSunaffected — they key oncode, which deliberately staysedge-kalshi-sports-mbp. Leaving the ledger's name alone is the right call, for exactly the reason the changelog gives.src/main.rs:974—vec!["elections", "events", "perps"]is reordered correctly for the precedingsort_unstable(). That is the one assertion in the diff where a mechanical find-and-replace would have produced a green-looking but wrong edit, and it was handled.
Nits
- The helpers keep the old name:
sports_row(),sports_channel_ids(),sports_ports_are_the_base_plus_the_channel_id,an_excluded_sports_channel_binds_no_socket, andlet sports = …. Defensible — they track the groupcode, which is still…-sports-mbp— andreconcile.rs's copy now says so in its doc comment. Butlet sports = feeds().find(|f| f.category == "events")reads oddly at each call site, and three copies ofsports_row()now exist (reconcile.rs,admin.rs,api.rs) while only one carries the explanation. One line in the other two would settle it. src/main.rs:968-972still has two consecutive comment lines that both open "The categories themselves…". Pre-existing, but this PR edits the second one — worth folding into one while you are in there.
f7d90b6 to
4a738a0
Compare
|
Rebased onto #151's branch so Read the diff as the ten files of the rename only. The base is still Nothing in the rename was touched: the rebase applied cleanly with no conflicts, and the tree against #151's branch is the same ten files as before. Verified on the rebased head:
|
|
Pushed The hosted document is now called out in the changelog. The entry says the rename does not take effect until the document at Nits. The two No behavior change; |
The universe that `edge-kalshi-sports-mbp` carries is event markets, of which sport is one kind, so `sports` named it too narrowly. The group `code` is unchanged: that name is the ledger's and the reconciler matches `doublezero status` on it, so renaming it here would have darkened the row silently. Nothing an operator passes changes -- `--channels`/`DZ_CHANNELS` key on the `code`, not the category. What changes is the producer-side identity every universe-scoped key is built from (the arbitration scope, the tape-owner rank, the catalog/book/history keys) plus the two read-only surfaces that report it: `category` in `/v1/status`'s `channels.rows[]` and in `/admin/channels`, and the `(category: ...)` suffix `/v1/products` appends when a symbol resolves in more than one universe under one Source ID. `category` is never serialized onto the WebSocket, so PROTOCOL.md is untouched. Every site that resolves the built-in document by category follows the value: the `feeds.rs` row lookup and its `(venue, category, kind) -> code` arm, `registry::sports_channel_ids`, and the channel-filter/admin/api/main/reconcile lookups with the fixtures keyed to the real row. Test data that only uses "sports" as an arbitrary category label is left alone, as are the helper and test names that name the group rather than the universe.
…to land The image bakes DZ_FEED_REGISTRY_URL in and a URL origin wins over the compiled-in copy, so on a default container src/ingest/registry.json is the fallback rather than the authority: until the document served at that URL carries "category": "events", every surface the entry names keeps reporting sports and the rename is invisible to an operator. The document is behind in the same way today — it carries neither the elections rows nor the Phoenix row. Also name the two `sports_row()` helpers after the group code they actually track, in the two copies whose doc comment did not say so, and fold the duplicated comment above the market-by-price universe assertion.
87e595e to
b67b865
Compare
There was a problem hiding this comment.
🟡 Changes recommended
The default container still consumes the hosted registry, and current operator documentation retains the old category name.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Renames the Kalshi sports universe category to events while preserving its ledger group code and channel-filter compatibility.
Changes:
- Updates the built-in registry and category-scoped tests.
- Adjusts feed selection, reconciliation, admin, and API fixtures.
- Documents the new category and deployment caveat.
File summaries
| File | Description |
|---|---|
src/ingest/registry.json |
Renames the built-in row category. |
src/ingest/registry.rs |
Updates registry test helpers. |
src/ingest/feeds.rs |
Updates feed and port expectations. |
src/ingest/channel_filter.rs |
Updates channel-filter tests. |
src/ingest/reconcile.rs |
Updates category-scoped reconciliation tests. |
src/sinks/api.rs |
Updates API status fixtures. |
src/sinks/admin.rs |
Updates admin fixtures. |
src/main.rs |
Updates feed-selection tests. |
CLAUDE.md |
Documents the renamed category. |
CHANGELOG.md |
Records the rename and deployment caveat. |
Review details
- Files reviewed: 10/10 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
The rename moved the `category` but left three sentences calling the
universe `sports`, all of them operator-facing: the elections rows'
`notes` ("separate from perps and sports", "the rule the sports row
uses", "the way the sports feeds are") and the socket-count caveat in
`docs/input-sources.md`. A reader following any of them looks for a
category that no longer resolves, so they now say `events` and the
input-sources line names the group `code` beside it, since that is the
value an operator passes to `--channels`. `sport` stays only where it
names a channel or a kind of market.
Also makes the hosted-document caveat say what that document actually
holds rather than which two rows it is missing: fetched today it carries
three rows, all Kalshi, so a default container ingests that venue and
nothing else — neither Hyperliquid row is there either, which the
previous wording implied was fine.
No code change; `cargo test --workspace` (1083 tests) and
`cargo clippy --all-targets` clean.
|
Pushed The prose now follows the value. Four sentences still called the universe The hosted-document caveat is now dated and says what the document holds. That caveat already existed (
|
|
|
rustls 0.23.40 accepted a TLS 1.3 handshake message that followed a key-changing message in the same record — a plaintext EncryptedExtensions behind the ServerHello — where RFC 8446 section 5.1 says to terminate with unexpected_message. The transcript stays authenticated, so it is not a way to alter or complete a handshake; it lets a peer send in plaintext what should have been encrypted without the connection being refused. Lockfile only: 0.23.45, with rustls-webpki 0.103.13 -> 0.103.15 alongside it. Nothing in this crate chose the version — it arrives through reqwest and tokio-tungstenite, which is also the whole of the exposure, since rustls is used for outbound client TLS only: the wss:// public inputs and the JSON-RPC and registry fetches. This process terminates no TLS itself. `cargo deny check advisories` is a required status, so this was red on every open pull request, #152 included, and none of them could have fixed it.
The universe
edge-kalshi-sports-mbpcarries is event markets, of which sport is one kind, sosportsnamed it too narrowly. The row'scategoryis nowevents.The group
codeis unchanged. That name is the ledger's and the reconciler matchesdoublezero statuson it, so renaming it here would have darkened the row silently — no warning, no failed bind, just a permanently-zerodz_receiver_up. The registry row now carries aNAMINGnote saying so, next to the value.The image bakes
DZ_FEED_REGISTRY_URLin (Dockerfile:107) and aUrlorigin wins over the compiled-in copy, so on a default containersrc/ingest/registry.jsonis the fallback, not the authority. Until the document served at that URL carries"category": "events", every surface below keeps reportingsportsand the rename is invisible to an operator.That document is behind in the same way today. Fetched 2026-09-18, it serves three rows, all Kalshi:
No Phoenix row, no
electionsrows and neither Hyperliquid row — so a default container ingests that venue and nothing else. Same shape as the stalelashay-*group codes the changelog already called out. ClearingDZ_FEED_REGISTRY_URLon one host makes the built-in document win, which is the single-host workaround; republishing is a separate rollout, outside this repo.What an operator has to do
Nothing.
--channels/DZ_CHANNELSkey on thecode, not the category, so every existing spec keeps working.What changes
The producer-side identity every universe-scoped key is built from — the arbitration scope (
(venue, category)), the tape-owner rank, and the catalog, book and history keys — plus the two read-only surfaces that report it:categoryin/v1/status'schannels.rows[]and inGET /admin/channels(category: …)suffix/v1/productsappends when a symbol resolves in more than one universe under one Source IDcategoryis producer-side only and is never serialized onto the WebSocket, so PROTOCOL.md is untouched.The sites that had to follow the value
Everything that resolves the built-in document by category — a stale literal there does not fail to compile, it stops resolving:
ingest/feeds.rs— the row lookup and the(venue, category, kind) -> codearmingest/registry.rs—sports_channel_ids(), which reads the document rather than the expanded rowsingest/channel_filter.rs,sinks/admin.rs,sinks/api.rs,main.rs— thef.category == …lookupsingest/reconcile.rs— theFeedKey, catalog, book and history keys of the tests built on the real row (sports_row())sinks/api.rs—feed_kind_for's registry rung filters on(venue, category); that test reported"unknown"until the fixture followedAnd the operator-facing prose, which a category rename silently contradicts rather than breaks — a sentence that still called it the
sportscategory sent a reader looking for one that no longer resolves:src/ingest/registry.json— the elections rows'notes: "separate from perps and sports" (it pairs two category names, and only one of them still existed), "the rule the sports row uses", "the way the sports feeds are"docs/input-sources.md— the socket-count caveat's "Kalshi's derived 31-channel sports row", now "events row (edge-kalshi-sports-mbp)", naming the groupcodebeside it since that is the value an operator passes to--channelsCHANGELOG.md— the/v1/productsfeed_kindentry's "single-kindsportscategory" and the elections entry's two mentions, both in the same unreleased section as this renameLeft alone deliberately:
sportwhere it names a channel or a kind of market (the row's ownNAMING/CHANNELSnotes, thesports.nflchannel labels), test data that only uses"sports"as an arbitrary category label (arbiter, authority, path_race, products, history, and the CLI's sample fixtures), and the helper/test names that name the group rather than the universe (sports_row,sports_channel_ids) — all three copies ofsports_row()now say in their doc comments that they are named for thecode.Testing
cargo test --workspace --lockedgreen (1083 tests, 0 failures),cargo clippy --all-targets --lockedclean,cargo fmt --checkclean.