A pass over 1.x before 1.0.0-alpha3, covering the release range rather than the increment: 1.0.0-alpha2 is 166 commits back, #3615981: Pre-alpha3 audit: refuse operator markup and scripted artwork on the place map, take the summary off its per-line reads, and put back the vocabulary, translations and docs the last 92 commits moved covered the first 92 of them, and the 74 since, which include the whole locking overhaul, the field rules, the booker messages and the bookable areas, have had no pass over them. Every linter and every job of the last pipeline is green, the five that are allowed to fail included, so nothing below was found by tooling.
1. The booking calendar cannot be operated without sight. The calendar element is the module's most public surface, built by both the calendar block and the resource field formatter. A day is a real button, but its accessible name is the day number alone, "14", with no month, no weekday and no state, and whether that day is available, partly available, full or already held by this visitor is carried by a CSS class and a color and by nothing else. So the one distinction a visitor is there to make is the one never spoken. The picked day is a class too, on a button with no aria-pressed. A day that cannot be booked is marked disabled, which the module argues against in its own comments elsewhere: stepper.js and venue-map.js both set aria-disabled instead, and both say why, that a disabled control loses focus and can no longer explain itself. The script rewrites its detail and summary on every pick and neither is a live region, where the slot booking and offer templates both ship one. In some eight hundred lines the calendar sets one ARIA attribute in total. Its stylesheet has neither a :focus-visible rule nor a prefers-reduced-motion block, both of which the seat map has. Sites running this have a statutory accessibility duty, so it is release blocking, and the tests go in with the fix.
2. One hold path is throttled and two are not. CalendarBookingForm calls itself "the authoritative gate for placing holds" and throttles per client, ten attempts a minute by default, on the stated grounds that each submit places real, capacity consuming holds against an anonymous facing surface. That reasoning holds equally for yoyaku_calendar.slot_claim and yoyaku_placement.place_op, the endpoints the two scripted surfaces actually use: both are reachable by anonymous visitors holding place booking, and both take slot locks and write booking rows. Neither consults flood control, and the flood event is registered in exactly one file. A CSRF header token is not a throttle, since every legitimate page hands one to every visitor. The check belongs where a hold is placed rather than on one surface that places it, so all three paths share one event and one setting; the form's docblock needs correcting in the same pass, because it is not the authoritative gate today.
3. The cost register is out of date, and one sentence in it describes a read that is gone. PERFORMANCE.md was measured on 2026-08-12, and #3618513: Anchor locks are taken in the order the plan reaches the rows, so two holds naming overlapping sets deadlock, #3617101: Keep what each scope has taken on its lock anchor, so a hold reads a row rather than a sum, #3618543: A failed rollback replaces the error it was handling, so a claim fails with nothing in the log and #3618572: A claim counts the whole hall again whenever it has held a seat have since changed exactly what it measures. Re-measured on this head with the same instrument, SQLite: holding a party of 1, 2, 6 and 12 costs 11, 14, 26 and 44 statements against the register's 10, 13, 25 and 43; seating a party of 1, 2 and 6 on a placed slot costs 16, 19 and 31 against 13, 16 and 28, while the reads of the place table fall from 5 to 1; and the orphan rule costs 23 where the register says 28. The hold figures were re-run on MySQL and came back identical, statement for statement and read for read, so the column is not an artifact of the driver. The register also explains the read that does not grow with the party as the sum of what a slot has taken, a grouped read over its bookings, and what the test counts today is the counter on the anchor rows that replaced it.
4. The operator facing figures predate the change that fixed them. docs/performance.md and the sizing guide are measured 2026-08-15, and the four issues above all landed on the 21st and 22nd. Between them they took failed claims on the load rig from around 1900 a run to under 70 and then to none, and 1360 server errors to none. An operator sizing a rush is therefore shown the machine as it behaved before the locking work, including the one figure the page insists on, the time to fill the house. The pages will say which head and which issues their figures predate; the rig re-run itself is a machine exercise rather than a code change, and the block it produces can be added when it has been done.
5. The metrics page is stale for the third release running. It reports 1116 files and 126,910 lines of code as of 2026-08-15; cloc with the command the page itself documents says 1264 and 146,671 today. It was 78 commits behind at the alpha2 audit and 92 at the alpha3 one, so regenerating it is going in the release checklist rather than staying an audit finding.
6. Money words survive where the rule says none may. The sweep on #3618369: The engine says sale where nothing is sold, and the documentation still calls a slot a session touched 48 files, one of which was a test, and around fifty occurrences remain: a class named SeatSoldOnceTest, a scenario named "a sold out session offers nothing" which breaks both vocabulary rules in one string, an assertion about a block no longer sold at the old price, and the load rig's own printed column header. Two are newer than that sweep, "oversell" in AreaCounterTest and "sold out" in src/LockAnchors.php, which is shipped engine code. Two say "wholesale" where the rule says outright. The idiomatic "what this buys", the sentence about unsold stock and the orphan rule's "rarely booked rather than rarely sold" are the documented keeps and stay as they are.
7. The code's own prose still calls a slot a session. docs/concepts.md publishes the rule, slot and never session, because session here is the visitor's web session, and the documentation now obeys it. The code does not: 945 comment lines across 230 files use the word, and a read through a sample of sixty found the large majority mean the slot. RequestBooker alone documents a BookingSlotInterface parameter as "The session being asked for", and does the like twenty-one times over. A minority genuinely mean the web session, the basket, the cookie jar, the cache context, and those have to survive, which is why this is a hand pass and not a substitution.
8. Seven passages describe a design nobody shipped. "The seat map used to guard on a busy flag", and six more like it in docs/steppers.md, docs/placement.md, docs/seating.md and docs/performance.md. Nothing is released, so there is no earlier version for a reader to have known. The alpha3 audit removed twelve of these.
9. Two things on the front page. yoyaku_presentation is a shipped top level module and is missing from the README's module table. And the requirements section explains why MySQL is mandatory by saying the hold reads what a slot has already taken with a locking select over the bookings, which is the read #3617101: Keep what each scope has taken on its lock anchor, so a hold reads a row rather than a sum deleted. The conclusion is unchanged and still correct; the mechanism named is not, and it is the paragraph somebody reads to decide whether the module fits.
10. The Drupal 12 lane has never run. OPT_IN_TEST_NEXT_MAJOR is on and the lane's jobs are manual, so nothing had played them. Played on this head, composer (next major) fails in 41 seconds: _LENIENT_ALLOW_LIST is empty, so composer resolves the real constraints of the dev dependencies that do not yet declare Drupal 12 and gives up, and the phpunit and phpstan next major jobs then fail without running a line. So nothing whatever is known about this module on Drupal 12, and the empty Project Update Bot draft on #3617449: Automated Drupal 12 compatibility fixes for yoyaku 1.x-dev is not evidence that there is nothing to do. Naming those dependencies in the lenient list is a one line change and rides here rather than opening a second merge request; the module's own core_version_requirement stays at ^11.3 until the lane is actually green.
11. Four smaller things. ModuleBoundariesTest is the only test class of 255 without #[RunTestsInSeparateProcesses], which is a fatal on the next major. BookingSlot declares a rule base field for the machine name of the recurrence rule that generated the slot, with a getter and a method on BookingSlotInterface, and nothing writes it and nothing reads it back: no form, no formatter, no query, no test. It is provisioned for yoyaku_recur, which is unbuilt, and every slot row carries the column for it; since the upgrade path before 1.0 is a reinstall, putting it back the day that module exists costs nothing. docs/recurrence.md opens by saying recurrence is shipped separately as yoyaku_recur, in the present tense, where the rest of the page says what that module will add and docs/architecture.md marks it planned. And three files end with more than one newline, which phpcs reports and CI never will, because CI does not scan Markdown.
Checked on this head and found sound, recorded so the next audit need not repeat it: phpcs over the module root under Drupal,DrupalPractice reports no error; phpstan at level 1 reports none; 125 of 125 entity queries carry accessCheck(); there is no eval, no unserialize and no interpolated SQL; the two routes with open access are HMAC capability URLs verified with hash_equals, with an expiry and no caching, and both public POST endpoints require a CSRF header token; the permissions that should be restricted are; a booker message is passed through Xss::filter before its placeholders are substituted and no template uses the raw filter; the one interpolation that lands inside an HTML attribute in the seat map is a grade color, and grade and allotment colors are both constrained to hex at the field; every shipped config file has schema, and the newest keys are typed; the French catalogue has no missing string, by extraction, and no dead one, by literal search, with the plurals intact and msgfmt clean.
AI-Generated: Yes (Claude Code was used to help draft this issue summary, and to write the changes and tests on the merge request. I reviewed the work and the figures quoted here were measured on this head.)
Issue fork yoyaku-3618603
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #4
mably commented