The period grid — a room, a period, a hard constraint
Every cell in the grid answers one question: which section, this room, this period
The period grid is rooms down one axis, periods across the other. Each cell holds exactly one section, and a partial-unique index on room, period, and day enforces that at the database — not as a UI validation a registrar could bypass with a bulk import, but as a write the database itself refuses to commit. Two assignments racing for the same room at the same period both get a clean, specific, retryable conflict. Bell-schedule and block-schedule fit is native: a period is a slot in the school’s own calendar shape, whether that’s seven fixed periods a day or a rotating A/B block.
Before an assignment is allowed into a cell, two more checks run: does this room carry the equipment tags this section requires, and does this room’s fire-marshal-posted occupancy cap cover this section’s roster size. Both are hard rejections, not soft warnings. A room posted for twenty-eight cannot take a roster of thirty-four. A course tagged for a kiln cannot land in a room that doesn’t have one.
The period grid, the equipment-match layer, and the occupancy-cap layer are built and production-ready. Shared-space rotation for the gym, the art room, and the auditorium runs on the same assignment engine; its calendar editing surface is in active development.
Rm 214 · Chem LabP3 · fume hood, gas lines
Rm 118 · Comp SciP4 · 24 stations
Gym AP2 · rotation: 4 PE sections
Rm 302 · ArtP5 · kiln