Writing
“Out of stock” and “we do not sell that here” are different answers.
One number and one optional flag decide which of the two a shopper hears. Getting it backwards costs a sale in one direction and costs trust in the other, and the wrong version of the sentence reads perfectly well — nothing breaks, the shopper just leaves with something untrue. This is the decision as it stands in the code, the three places it has to be repeated, and the part of it we still cannot answer.
Drafted by a model and checked line by line against the files it names before it went up, which is the rule this directory set for itself: if a machine wrote it, the piece says so at the top. Every claim below points at a file in the app — src/kb.ts, src/araclar.ts, scripts/kb-uret.mjs and test/yayinlanmamisUrun.test.ts — so a reader who disagrees has something to open. The identifiers in those files are Turkish; the sentences the shopper hears are not.
Two fields, one question
A product in Refik’s knowledge base carries a stock count and, separately, an optional flag saying whether it has a page on the storefront. Neither field answers the question a shopper is actually asking, which is whether they can buy the thing right now. Both of them together do, and the app writes that down once rather than letting each caller spell it out:
export function vitrindeSatilir(u: Urun): boolean {
return u.stok > 0 && u.yayinda !== false;
}
The reason the two halves are kept apart is that they fail differently. A product with no page can sit in the catalogue with a full shelf behind it; a product with a page can be empty. Collapse them into one field and you get an assistant that says whichever of the two sentences happens to be wired up — and one of those sentences is a promise about restocking that nobody can keep, because there is nothing to restock into.
A missing field is not a no
The flag is optional, and its absence is read as “published” rather than “unpublished”. That is the comparison in the line above: not equal to false, rather than equal to true. It looks like the careless choice and it is the deliberate one. A catalogue that carries no publication signal at all — because the dump it came from never asked for the field — would otherwise have every working product link on the storefront switched off at once, on the strength of a field nobody filled in.
So the app treats unknown as available and shouts about the unknown somewhere else, which is the subject of a later section. The alternative is to treat unknown as unavailable and go quiet, and a quiet failure that hides the whole catalogue is worse than a loud one that hides nothing.
Three situations, three sentences
When a shopper names a product that cannot be bought, the search tool returns the same kind of result in every case, and a different instruction with it. The instruction is the interesting part, because it is where the distinction either survives or gets flattened.
| Catalogue state | What Refik is told to say | What it is told not to say |
|---|---|---|
| Has a page, stock above zero | The ordinary answer, with a link to the product | — |
| Has a page, stock at zero | That it is out of stock right now, followed by the alternatives | Nothing extra; this is the sentence everyone already writes |
| No page, stock above zero | That the product exists and is in stock, but is not sold on this online store: there is no page to link and nothing to order | That it is out of stock, and that it will come back |
| No page, stock at zero | That it is not sold on this online store | Anything at all about its stock, in either direction |
The last row is the one that took the longest to get right, and it is the one a single sentence cannot cover. A product that has no page and no stock is doubly unavailable, and there is no honest claim to make about the shelf: saying it is in stock and saying it has run out are equally unverifiable, because the number in the catalogue is not describing anything the shopper could reach. So the instruction refuses both. The absence of a claim is the claim.
The third row is the expensive one. A product with a full shelf and no page is exactly the case where “out of stock” is a plain falsehood, and it is also the case a stock check on its own will never catch — the number is healthy, so a gate that looks at the number waves it through.
The same distinction, three times over
Writing the rule once is not the same as applying it once. Three separate things a shopper can do run into an unbuyable product, and each of them had to be told the difference in its own words:
- Asking for the product. The search tool returns the product by name — a shopper who asked for it by name is owed that much — with one of the sentences from the table above and a set of alternatives.
- Asking for a routine built around it. If the product cannot anchor a routine, the tool refuses and says which of four reasons applies: it is out of stock, or it is not sold on this online store, or it is not a step of any routine we build, or nothing else fits around it. Four, kept apart, because “we could not do that” on its own tells the shopper nothing they can act on — two of those reasons are about the product and two are about the shape of the catalogue.
- Asking to be told when it comes back. Refused for a product with no page, and refused with the reason: there is nothing to notify anyone about.
All three live in the same file, and the sentence about not being sold on this store appears in it three times — once per caller, not once as a shared constant. That is a cost, and it is written down as one: three copies of a sentence drift, and the only thing keeping them together today is a test file.
Which gate runs first
In the back-in-stock request the page check runs before the stock check, and the order is load-bearing rather than tidy. Reverse the two and a product with a full shelf and no page takes the other branch, where the app cheerfully replies that the shopper can order it right now. They cannot: there is no page to order it from. The stock number was true and the sentence built on it was false.
This is the same shape as the row in the table, arriving in a different tool, which is why it is worth stating as an order rather than a condition. A condition can be reordered by anyone tidying up; an order that has a reason next to it tends to survive.
What the shopper is not shown
Two things are deliberately kept out of the answer, and both of them are things it would be easy to leak by accident.
The first is the product’s handle — the identifier the storefront builds a URL from. For a product with no page it is never passed to the model, because a model holding a handle will construct the address out of it, and that address returns nothing. Refusing to link is not enough if you hand over the parts.
The second is the publication state itself, in the product summary that goes into the prompt. The summary for a product with no page and the summary for an ordinary product carry the same field names, in the same order: the state adds nothing to the prompt and is expressed only once, in the instruction. An earlier version said the product was in stock in the summary and told the model to make no claim about stock in the instruction, in the same reply. Both halves were defensible on their own. Together they were a contradiction, and the model had to pick one.
That is the general shape of the problem. Almost nothing here fails loudly; it fails by saying two true-sounding things that cannot both hold, and the reader has no way to tell which one to believe.
None is not the same as unknown
The tool that builds the knowledge base prints two numbers about this, not one: how many products are marked as having no page, and how many products carried a publication signal at all. They are separated on purpose, because “no products are unpublished” and “nothing in this catalogue mentions publication” produce the same silence and mean opposite things. One says the gate looked and found nothing; the other says the gate was never wired up.
When the signal is missing entirely the build does not shrug. It prints a warning saying that every product in this catalogue will be treated as published, that an unpublished product can therefore still be linked, and which field to add to the query that produced the dump. When the signal is only partly there it says how many products carry it and states plainly that the rest were counted as published.
None of that is a check that can fail the build, and calling it one would be the kind of overstatement this directory exists to avoid. It is a number on a console, read by whoever ran the tool. The thing it buys is that a catalogue with the gate switched off looks different from a catalogue with the gate switched on and nothing to catch — which, before the two numbers were split apart, it did not.
What this still does not tell you
Four gaps, none of them closed by any of the above.
- Why the product has no page. The catalogue carries a flag, not a reason. Withdrawn, seasonal, never launched, sold only in a shop, or unpublished by mistake all arrive as the same value, and the assistant says the same sentence for all five. There is no field for the difference, so there is no honest way to guess at it.
- Whether the flag is current. It comes from a catalogue dump. Between dumps, a product that went back on sale reads as unavailable, and the app has no way to notice on its own. The sentence is as fresh as the file.
- What to do for the shopper. A product with no page has nowhere to send the request to be told when it returns, so the request is refused rather than quietly filed. That is honest and it is still a dead end: they wanted the thing, and the answer is that we cannot help with that one.
- Whether the alternatives are any good. Every case above ends by offering something else, chosen by matching what the product was for. Whether that is a useful substitute is a judgement about the products, and nothing in this decision measures it.
What holds the four sentences apart today is one test file with more than two dozen checks in it, several of which run the whole tool rather than calling the decision on its own. That distinction is deliberate and it is written down in the file: a check that read only the instruction would have stayed green while the rest of the same reply contradicted it, so the later checks count the whole thing. That file is the reason this post can describe behaviour instead of intent, and it is the reason the sentences have not quietly merged back into one.
If you want the rest of what this site refuses to claim, the approach page lists it, and the answers themselves are shown in a recorded session. The rule this piece follows about naming its sources is on the writing index.