For the 21 entries, stop reading the devtools list - it truncates nested objects, so filters:{...} will look identical for keys that are nothing alike. Dump the hashes instead:
Diff two of those strings and the culprit falls out immediately. Nine times out of ten it is a Date somewhere in the filter object being rebuilt on render, because it serialises down to the millisecond.
Whatever you get, wear it for three long runs before race day and configure the screens then. Fiddling with a new device in the start pen is a classic way to spend the first 5k staring at your own wrist.
On the $4.80: that is not 38 calls at 12 cents, it is the transcript being resent every step and growing each time. Step 38 pays for everything that happened in steps 1 to 37. Cost goes up roughly with the square of step count, which is why a runaway loop is so much more expensive than it looks.
Two mitigations that are independent of fixing the loop: turn on prompt caching so the stable prefix is not repriced every step, and truncate tool results aggressively - three chunks at 400 tokens each, repeated 38 times, is most of your bill and none of your answer quality.
First, split the question in half, because you are guessing across two systems. Open devtools, click the query, and look at the cached data after the refetch lands.
Cache holds the new value and the UI shows the old one: this is a render problem, not a cache problem. Ninety per cent of the time it is mirrored state - somebody did const [rows, setRows] = useState(data) or an uncontrolled defaultValue on the cell, and the initialiser only ever runs once.
Cache holds the old value: then the refetch you are watching belongs to a different key than the one your component subscribes to, or something wrote the old value back after it landed.
That one check saves an afternoon. My money is on the first, given "navigate away and back" fixes it - remounting is exactly what re-runs a useState initialiser.
It is nearly always that. The tell is "works after navigation" - a cache bug does not care whether the component remounted, and a mirrored-state bug cares about nothing else.
A torch straight onto the surface, or a torch plus the pan, gets you a crust with almost no heat penetration at all. Faff to set up, and there is a knack to avoiding a fuel taste by keeping the flame moving and not lingering, but it is the most controllable option I have tried.
Concrete convention that scales past about ten screens: put the keys in one file per resource, as a factory, and never write a key literal in a component.
export const orderKeys = {
all: ['orders'] as const,
list: (f: Filters) => [...orderKeys.all, 'list', f] as const,
detail: (id: string) => [...orderKeys.all, 'detail', id] as const,
}
It sounds like ceremony for a small app and it pays for itself the first time you need to find every place that reads orders. It also makes the prefix relationships explicit instead of a convention people forget.
Whatever you change, do not evaluate it on vibes. Save 200 real tool calls from your logs, replay them through the new schema, and count. Every one of these threads has somebody who fixed it with a description tweak and somebody else for whom that changed nothing, and the difference is always that neither of them measured.
Worth checking whether you also have a loading.tsx in that route segment, because that is the boundary you are currently hitting. It is convenient for the initial route transition and it is completely the wrong granularity for a single card - once every leaf has its own boundary, the segment-level one should be showing up rarely.
On the safety side, since people worry about 145F chicken, the pasteurisation tables are based on time at temperature rather than a single number, and an inch and a half breast held at 145F is well past what those tables ask for. Look up a proper reference table rather than trusting a forum comment though, including mine, and if you are cooking for someone with a compromised immune system take the conservative route.
The bit I keep tripping on is conflict handling. If a technician edits a job offline and someone edits it in the office, replaying my mutation just clobbers them. Does the paused mutation approach give you anything for that or is it on me?
Where does a half-finished job form sit in that split? It is client-owned while being typed and server-owned the moment it is submitted, and that transition is where our code gets ugly.
The headline number is usually the Limit rating, and under ISO 23537 that is modelled on a standard man curled up and not shivering. The Comfort rating is modelled on a standard woman sleeping relaxed, and it is several degrees warmer. So if you were cold in a "0C" bag at 0C, nothing malfunctioned: you read a barely-surviving number as a sleep-well number. Buy on Comfort. If a listing only gives you one number and won't tell you which, that's a listing to walk away from.
Exactly that. The standard also assumes you're on an insulated mat, wearing a base layer, and not already cold when you get in. All three of those get quietly skipped.
If the bag is genuinely inflating rather than floating on trapped air, that is worth a second look. A pinhole from a bone edge or a poorly sealed corner lets water in slowly, and the tell is that the bag feels heavy and the water looks cloudy. Double bagging anything with a bone or a sharp trimmed edge takes ten seconds.
Also try it against the inspector rather than your real client. Half the clients out there swallow protocol errors and render an empty tool list, which is how you end up with "no error anywhere". The inspector will show you the raw JSON-RPC in both directions and the problem is usually obvious within thirty seconds of seeing the actual bytes.
Also dry the tent properly when you get home, every single time. Packing it damp is how a perfectly good tent becomes a mouldy tent in two seasons, and then people blame the design and buy another one.
There are no mutations today. Within a year somebody will want to acknowledge an alert from the dashboard, and then you will want the mutation cache, the invalidation and the devtools. That is not a knockout argument but "we do not need feature X" on an internal tool has a short shelf life. The bundle difference between the two is not the thing that will decide whether this app is fast.