> For the complete documentation index, see [llms.txt](https://docs.trover.tech/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.trover.tech/engineering/verify-2026-09-05-queue-loading-floor.md).

# Verify — queue drain, loading states, floor grid

## Migration this round

**No migration.** Nothing in `packages/db/migrations` changed. Do **not** run the `migrate` service.

## What changed

| Area   | Change                                                                |
| ------ | --------------------------------------------------------------------- |
| Worker | Stale `nft-canary-reconcile` jobs are now actually dropped            |
| Web    | Shared loading primitives; terminal, trending and discover use them   |
| Web    | Collection floor no longer duplicates a token that has several orders |
| Web    | Hover drawer on each floor card with a one-click **buy now**          |

## Deploy

Frontend is Vercel and deploys itself on push to `main`. The backend needs one service rebuilt, plus one that has been stale for a week.

```bash
ssh trevor-server
cd ~/trover
git pull

# The worker carries the canary-job guard fix.
docker compose -f docker-compose.yml -f docker-compose.production.yml build worker
docker compose -f docker-compose.yml -f docker-compose.production.yml up -d --force-recreate worker
```

### The stale canary container — rebuild this

`trover-opensea-browser-canary-1` was built **2026-08-29** and has been running pre-fix code ever since. It enqueues a full `sync_nft_collection` for `button-presser` every ten seconds; each one fails on OpenSea's rate limit and the next one is already queued. That is the entire 234-job backlog, and it is also burning the shared OpenSea rate budget that real collection syncs need.

```bash
docker compose -f docker-compose.yml -f docker-compose.opensea-canary.yml build opensea-browser-canary
docker compose -f docker-compose.yml -f docker-compose.opensea-canary.yml up -d --force-recreate opensea-browser-canary
```

The current source already coalesces to one job per collection per five minutes and no longer enqueues on DOM samples at all, so the rebuild alone stops the refill. Until it is rebuilt, the worker guard keeps the jobs from executing but they will still accumulate in Redis.

## Verify

### 1. The queue stays drained

```bash
ssh trevor-server 'cd ~/trover && docker compose -f docker-compose.yml -f docker-compose.production.yml exec -T redis \
  redis-cli llen bull:trover-nft-market:wait'
```

Was **234**, drained to **6**. After the canary rebuild it should stay in single or low double digits. Run it twice a few minutes apart — a number that climbs by roughly 6 per minute means the canary container was not rebuilt.

Job names still queued, if you want to see what they are:

```bash
ssh trevor-server 'cd ~/trover && docker compose -f docker-compose.yml -f docker-compose.production.yml exec -T redis sh -lc \
  "for id in \$(redis-cli lrange bull:trover-nft-market:wait 0 -1); do redis-cli hget bull:trover-nft-market:\$id name; done | sort | uniq -c | sort -rn"'
```

No line should read `nft-canary-reconcile`.

### 2. Loading states

Hard-reload `https://dex.trover.tech/nfts/robinhood/argonauts` and watch the first few seconds. Previously every stat tile read `—`, the panel said "no active listings indexed yet" and the grid was blank — for about twenty seconds, which is indistinguishable from a dead collection.

Expect instead:

* stat tiles shimmer rather than showing an em dash
* the left panel header reads **Loading orderbook** with skeleton rows
* the floor grid shows skeleton cards and "loading items…"
* once loaded, an em dash means what it always meant: measured, and empty

### 3. No duplicate NFTs on the collection floor

Scroll to **collection floor**. Before this change token **#1814** appeared twice, at 0.355 ETH and 0.357 ETH, because it had two live orders and the grid keyed rows by order hash.

```js
// paste in the browser console on the collection page
const ids = [...document.querySelectorAll('[class*="group relative"] p.truncate')].map(n => n.textContent);
ids.length - new Set(ids).size   // expect 0
```

A token that genuinely has several live orders now shows one card at the cheapest price, labelled `cheapest of N live orders` in its hover drawer.

### 4. One-click buy

Hover any priced card in the floor grid. A drawer slides up out of the artwork with the token id, its price, and a green **buy now**. Clicking it targets that exact order and prepares the quote in a single click — it does not execute without the wallet confirmation that already gates every trade.

Keyboard: tab to a card and the same drawer opens on focus.

## Not done this round

* **Metadata coverage.** Several floor cards still render a broken-image placeholder because only a fraction of listed tokens have metadata resolved. Three grey placeholders in a row is probably part of what read as "duplicates". Prioritising the metadata sweep toward *listed* tokens is the fix and is still unstarted.
* **Whether the OpenSea browser canary should run at all.** It has produced a week of rate-limit failures and nothing else observable. Rebuilding it stops the queue flood; deciding to keep it is a separate call.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.trover.tech/engineering/verify-2026-09-05-queue-loading-floor.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
