Issue #3618536: A booker who asked for a number is told a seat they never chose is taken, and hears about a repair they never asked for
PlaceBookable refuses a seat that has gone, under the lock, and says "This place is already taken for this slot." That sentence is right for the booker it was written for: someone who pointed at that seat on the plan and lost it by a moment. Every line carrying a place goes through the same constraint, though, and the engine composes places itself, so a booker who pressed a stepper was answered in a seat-clicker's words about a seat they never saw.
The change. The constraint tells them apart by place_chosen, which it already reads a few lines earlier. Only a line that says outright it was composed reads as given: an operator at the counter and a client of the API both name a seat and say nothing about who chose it, and both of them chose it, so silence means chosen. The engine is the one caller that knows the booker never saw the seat, and it sets the flag to FALSE on every line it composes. A booker who chose the seat still gets the sentence about the seat. A booker who asked for a number is told "There is no longer room for what you asked for.", which is what is left of the truth for them.
And a refused rearrangement now says nothing at all. It is a repair nobody asked for: the exchange is all or nothing inside one transaction, so a refusal leaves the booker holding exactly the places and the very lines they held a moment earlier. That their party sits apart is true, and it was already said in their own words by the closeness sentence on the claim that placed them, so repeating it here would land on every press of a stepper that failed to improve on it.
The silence is honest only while the exchange stays all or nothing, so the test asserts both halves: nothing said, and the same line ids and the same places afterwards. If a refusal could ever leave a booker with less, that test is what would say so.
Tests. TakenPlaceSentenceTest covers the two readers of one refusal. SeatTogetherTest::testRefusedRearrangementSaysNothingAndChangesNothing covers the silence, forcing the refusal by freezing the order, since a race between the read and the lock cannot be staged in one process. Both were confirmed to fail against 1.x and to pass here; the chosen-seat case passes on both, which is what a regression guard should do.
Not changed, and deliberately. PlaceBookable's other sentences ("The chosen place does not exist.", "This place is not open in the chosen placement configuration.") can also be reached by a composed line, but reaching them means the engine composed something invalid, which is an internal fault rather than a race a booker should be told about. Those belong with the fallback in BookerRefusal, not with a reworded sentence.
Follows [#3617964], whose refusal() and said() seam this hangs on.
AI-Generated: Yes (Claude Code was used to draft the issue summary and to write the code, the documentation, the translation and the tests on this merge request. I review and run the work before it is merged.)