Issue #3618720: Call a partition of a resource a partition, in the engine and across the seam
The engine always had the concept — a subdivision a request can be confined to, that carries its own count, and that holds serialize on — and called it an area, a word about space. An engine booking a hotel night or a projector has no areas, and a reader who learns the word reaches for it again. That's not idle: the comment directly above one of the reach-ins fixed in [#3618663] talked about "a picker that had the hall open", and the reach-in followed the words.
Revenue management calls it a sub-inventory — accurate, clumsy in an identifier. The engine's own docs already had the short form: "What partitions the seats is the part of the house they sit in". So partition.
Renamed in the engine and wherever the concept crosses the seam: the provider interface and its method, the line and request key, the two flags a retry and an assignment set, the service tag, and the placement module's implementation. LockAnchors::SCOPE_SECTION is now SCOPE_PARTITION.
Two words, two layers, one translation point. partition is the engine's word for something it carries and never opens. area stays placement's word and the booker's: the picker's option groups, the JS, the HTTP claim body, the admin form, and every *AreaTest class name. SlotClaimController::wanted() is the single point that reads the wire's area and emits the engine's partition; everything downstream of it speaks partition, everything upstream speaks area.
Three more contracts were publishing another module's shape. [#3618663] took the engine out of another module's schema in two places and missed these: the hold interface, the request booker, the composer and the move value object all published array{section: int, subsection: int|null} in their own signatures, for a value the engine carries and never opens. They say mixed now, with the reason where the shape used to be.
While renaming the key, OfferClaim and SelectionBooker — both downstream of the translation point — were still reading the old one, so a claim's partition arrived empty and every confinement was silently dropped. OfferClaim also grouped its moves on $area['section'], reaching inside a value it is not allowed to open; it groups on the serialized partition now, so the only question it asks of two partitions is whether they are the same one.
Deliberately excluded. SCOPE_PARTITION keeps the value 'section': the string is a key in the anchor table, and changing it orphans a running site's rows until they are recounted. That is a step of its own, with the recount planned rather than discovered. Also untouched: the four lists hardcoding another module's field names, and the theatre in the engine's prose (seat 171, hall 39, "the house" 32).
Also in here, both found by this branch's own pipeline: AssignedAreaPickerTest clicked the panel through a handle it had already found, and a redraw landing between the two round trips left it clicking a node that had left the document — the failure this trait's own assertValueIs() docblock was written about. It has a click twin now, clickFromThePage(). And SlotBookingPageTest passed $this->page() straight into renderRoot(), which takes its argument by reference, for two PHP notices.
Verification. Full pipeline green on both lanes, kernel and functional, with composer (next major) played. All ten hold-cost figures identical, compared one by one: renaming moves no statement. phpstan level 3 [OK], phpcs and cspell clean.