Issue #3618663: Take the engine out of another module's schema, in the two places it reached in

The engine named fields it does not own, and assembled a shape it should not be able to spell, in two places.

anchorScopes() rebuilt a saved line so the scope providers could be asked about what a hold is giving up, and did it by reading place and section off the booking behind hasField() guards. Those guards are the tell: the engine knew the columns might not be there, which is it knowing they are not its own. The saved bookings now go to the providers as they are, and each reads its own fields. The engine builds only slot, tariff, quantity and allotment.

assignAreas() took a provider's candidate and wrote ['section' => $id, 'subsection' => NULL]. A provider now answers with areas rather than ids, and the engine carries one whole without opening it.

Both contracts lose the vocabulary too: HoldAreaProviderInterface answered in "area ids", and LockAnchorScopeProviderInterface spelled area{section: int, subsection: int|null} in its own @param — the engine publishing another module's shape as part of its contract.

A guard in ModuleBoundariesTest asserts the engine names none of place, place_chosen, subsection, section. Seen to fail against the code as it stood, naming all three real ones. One exception, named rather than hidden: LockAnchors::SCOPE_SECTION is a scope constant whose value is stored in the anchor table, and the engine drops those scopes when a hold takes a whole slot. Why that scope alone is droppable turns on which anchors carry counters a hold will write, which is the one thing here that deadlocks when got wrong, so it is left for a change of its own.

One regression, caught and worth recording. The placement's scope provider takes the hold-area provider as a direct dependency and cast each answer with (int). With areas coming back instead of ids, that cast produced 1, so a party too big for one block waited on section:1 instead of both blocks. It reads its own shape now. Nothing found that call by grepping the interface name — the service is wired to the concrete class.

Cost: every figure in the hold cost log identical before and after, all ten scenarios, compared figure by figure rather than merely green.

Local, MySQL: 15 classes, 124 tests — ModuleBoundariesTest, LockAnchorScopeTest, SeatTogetherTest, SeatWithoutOrphansTest, SeatingShareOutTest, PlacesApartTest, SeatTakenOnceTest, AreaCounterTest, AreaCountsKeptTest, PlacesInPlayTest, PlaceBookingTest, PoolOpTest, PlaceReleaseCostTest, AllotmentQueryBudgetTest, BookingChannelRestrictionTest. phpcs clean. The whole suite belongs to CI.

Merge request reports

Loading
Loading