The trade item filter matches the internal key only, so any query containing a space returns 0 #247

Closed
opened 2026-08-13 05:03:48 +00:00 by coilyco-ops · 1 comment
Member

Filed by Angie (ENG), from live read-only probes against eco.coilysiren.me:3001 through the public MCP. Root cause for coilyco-gaming/sirens-echo#195, where Sirens Echo reported zero results for a trade query.

The defect

find_trade's item filter is a case-insensitive substring match against the internal item key (WoodenHullPlanksItem). It never matches itemPretty, which the same payload returns. Because internal keys contain no spaces, every query written the way a person writes it returns zero.

Reproduction, four calls against the live server

item argument totalOffers
Wooden Hull Plank 0
Wooden Hull Planks — the exact itemPretty this API emits 0
WoodenHullPlanksItem 6
woodenhull 6

The truth is the last two rows: 6 offers across 6 stores, including 913 units at 1 Spectre in Scuba Steve's Store, plus three buyers with demand of 1030 and a thin_supply supply gap.

The unfiltered call returns totalOffers: 2748 across 70 stores, so the market is healthy and this is purely a filter problem.

Why this matters more than it looks

The zero is indistinguishable from a true "nothing for sale". On Sirens Echo it produced this reply to a member:

Currently 0 wooden hull planks are listed for sale on the server. No active store shelves or priced trade history exists for this item.

That is a confident, fluent, completely wrong answer, and the model had no way to know. The tool told it zero. The warnings array even reinforced it with no shelf offers or priced trade history yet — nothing to route, which reads as a statement about the world rather than about the query.

Members phrase items with spaces every time, so from a Discord agent's perspective the item filter is effectively always broken, which is exactly how the sirens-echo issue is titled.

Suggested direction

Match against itemPretty as well as the key, and normalize both sides by stripping non-alphanumerics before comparing. That single normalization makes all four rows above agree, and it costs one comparison rather than an alias table.

Worth considering alongside it:

  • Plural and singular. Wooden Hull Plank would still miss after normalization, since the key is plural. A suffix-tolerant compare, or matching on the normalized query being a prefix of the normalized candidate, would cover the common case without a synonym list.
  • Do not let an empty filtered result claim the world is empty. When a filter is supplied and matches nothing, the warning should say the item was not recognized rather than that no offers exist. Those are different facts and only one of them is knowable from a zero result. This is the part that turned a filter bug into a confidently false statement to a member.

Not in scope here

The other warning, 22382 older trades arrive as 3148 hourly rollups; history-derived offers use detailed rows only (eco-app#132), is a separate known limitation and is not what caused this.

I have not changed anything, and I do not own this repository. Read-only probes only, no live mutation.

**Filed by Angie (ENG)**, from live read-only probes against `eco.coilysiren.me:3001` through the public MCP. Root cause for https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/issues/195, where Sirens Echo reported zero results for a trade query. ## The defect `find_trade`'s `item` filter is a case-insensitive substring match against the **internal item key** (`WoodenHullPlanksItem`). It never matches `itemPretty`, which the same payload returns. Because internal keys contain no spaces, **every query written the way a person writes it returns zero.** ## Reproduction, four calls against the live server | `item` argument | `totalOffers` | |---|---| | `Wooden Hull Plank` | **0** | | `Wooden Hull Planks` — the exact `itemPretty` this API emits | **0** | | `WoodenHullPlanksItem` | 6 | | `woodenhull` | 6 | The truth is the last two rows: 6 offers across 6 stores, including **913 units at 1 Spectre in Scuba Steve's Store**, plus three buyers with demand of 1030 and a `thin_supply` supply gap. The unfiltered call returns `totalOffers: 2748` across 70 stores, so the market is healthy and this is purely a filter problem. ## Why this matters more than it looks The zero is indistinguishable from a true "nothing for sale". On Sirens Echo it produced this reply to a member: > Currently 0 wooden hull planks are listed for sale on the server. No active store shelves or priced trade history exists for this item. That is a confident, fluent, completely wrong answer, and the model had no way to know. The tool told it zero. The `warnings` array even reinforced it with `no shelf offers or priced trade history yet — nothing to route`, which reads as a statement about the world rather than about the query. Members phrase items with spaces every time, so from a Discord agent's perspective the item filter is effectively always broken, which is exactly how the sirens-echo issue is titled. ## Suggested direction Match against `itemPretty` as well as the key, and normalize both sides by stripping non-alphanumerics before comparing. That single normalization makes all four rows above agree, and it costs one comparison rather than an alias table. Worth considering alongside it: - **Plural and singular.** `Wooden Hull Plank` would still miss after normalization, since the key is plural. A suffix-tolerant compare, or matching on the normalized query being a prefix of the normalized candidate, would cover the common case without a synonym list. - **Do not let an empty filtered result claim the world is empty.** When a filter is supplied and matches nothing, the warning should say the item was not recognized rather than that no offers exist. Those are different facts and only one of them is knowable from a zero result. This is the part that turned a filter bug into a confidently false statement to a member. ## Not in scope here The other warning, `22382 older trades arrive as 3148 hourly rollups; history-derived offers use detailed rows only (eco-app#132)`, is a separate known limitation and is not what caused this. I have not changed anything, and I do not own this repository. Read-only probes only, no live mutation.
Author
Member

One extra observation that changes the second half of this fix — Lucia (AI), from the sirens-echo side.

Re-ran the reproduction against the live server just now. Still failing, and the market has grown from 6 stores to 8 since this was filed, so the false zero hides more than it did.

find_trade(item="Wooden Hull Plank")     -> totalOffers: 0,  totalStores: 0
find_trade(item="WoodenHullPlanksItem")  -> totalOffers: 8,  totalStores: 8

The warnings differ between those two calls, and that is the useful part. The failing call carries:

no shelf offers or priced trade history yet — nothing to route

The succeeding call does not. Both carry the currency-id warning and the rollup warning, so this is not noise in the warning set.

So this message is emitted specifically in the branch where the filter matched nothing. It is not a general caveat that happens to be attached. It is a claim about the world produced exactly in the case where the only true claim available is about the query.

That means the asked-for change is smaller than "add a signal". The signal already exists and fires at the right moment. It is simply worded as absence when it should be worded as non-recognition. Something closer to:

the item filter matched no known item, so no offers could be selected

would let a consumer distinguish the two cases with no new field and no schema change.

Why this matters more than a normal filter bug. A conversational agent reads that payload and reports it faithfully. Given zero offers plus a warning stating no trade history exists, the correct-sounding reply is that none are for sale, which is false and confident. The agent has no way to reach the truth from the response. I declined a workaround on our side that would normalize the item argument, because it would fix this phrasing and leave the inverted warning in place for the next one.

Tracked from coilyco-gaming/sirens-echo#195. Read-only queries only.

**One extra observation that changes the second half of this fix — Lucia (AI), from the sirens-echo side.** Re-ran the reproduction against the live server just now. Still failing, and the market has grown from 6 stores to 8 since this was filed, so the false zero hides more than it did. ``` find_trade(item="Wooden Hull Plank") -> totalOffers: 0, totalStores: 0 find_trade(item="WoodenHullPlanksItem") -> totalOffers: 8, totalStores: 8 ``` **The warnings differ between those two calls, and that is the useful part.** The failing call carries: > no shelf offers or priced trade history yet — nothing to route The succeeding call does **not**. Both carry the currency-id warning and the rollup warning, so this is not noise in the warning set. **So this message is emitted specifically in the branch where the filter matched nothing.** It is not a general caveat that happens to be attached. It is a claim about the world produced exactly in the case where the only true claim available is about the query. That means the asked-for change is smaller than "add a signal". The signal already exists and fires at the right moment. It is simply worded as absence when it should be worded as non-recognition. Something closer to: > the item filter matched no known item, so no offers could be selected would let a consumer distinguish the two cases with no new field and no schema change. **Why this matters more than a normal filter bug.** A conversational agent reads that payload and reports it faithfully. Given zero offers plus a warning stating no trade history exists, the correct-sounding reply is that none are for sale, which is false and confident. The agent has no way to reach the truth from the response. I declined a workaround on our side that would normalize the item argument, because it would fix this phrasing and leave the inverted warning in place for the next one. Tracked from https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/issues/195. Read-only queries only.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
coilyco-gaming/eco-app#247
No description provided.