The problem

For adult student clubs, a cancellation can create more than one gap. Someone qualified to cover the missing role may already have another assignment. Replacing one name without checking the whole roster can introduce an overlap or leave another role uncovered. RosterFair explores small reassignment chains and makes their consequences inspectable. This prototype has not yet been evaluated with real organizers.

What it does

The organizer reviews explicit capabilities, availability, existing assignments and pinned commitments, then records a cancellation. The canceled volunteer becomes unavailable for that interval. RosterFair compares feasible options, explains every changed assignment and shows workload tradeoffs before the organizer applies one. Search and selection alone change nothing; Undo restores the previous saved roster.

In the synthetic example, Maya cancels the welcome desk. Eli can cover welcome but is assigned to tools. Moving Eli to welcome and Bea into tools restores all five slots while changing one existing assignment. Another option moves Noor to welcome and Alex into repair, but exceeds Alex's preferred workload.

The interactive before/after preview lets the organizer compare both options, inspect the reassignment chain and switch between changed roles and the full roster. Pinned assignments and remaining gaps stay explicit. The proposal is labeled Not applied until the organizer explicitly applies it.

The objective order is explicit: maximize covered slots, minimize changes to occupied slots, then minimize squared minutes above stated workload preferences. Capabilities, availability, absences, non-overlap and pins are hard constraints; workload preferences are soft. Contradictory pins are reported rather than relaxed. This policy does not claim objective social fairness or infer sensitive traits.

How it was built and tested

React, TypeScript and Vite provide the interface. Bounded exact backtracking runs in a Web Worker, supporting one event-local day with up to 12 volunteers and 12 shifts. The search budget is 200,000 nodes or 1.5 seconds. optimal proves the best ranking for the supplied instance; limited makes no optimality or impossibility claim; invalid reports conflicting fixed constraints or malformed data.

The exact v0.3.1 source passed 35 domain/state tests and 23 production Chromium checks, plus lint, types and build, in GitHub CI. The domain evidence includes comparison with an independent exhaustive oracle on 240 tiny fixtures. Checks cover impossible schedules, overlaps, capabilities, availability, pins, repeated apply/undo/edit flows, files, interactive repair previews, keyboard use, responsive layouts and reduced motion. These finite checks do not prove every possible input or certify accessibility.

Keeping work and recovering safely

Manual JSON export and import let organizers reopen a saved roster after closing the tab. Strict versioned validation caps files at 128 KiB and rejects malformed or unsupported data without replacing the roster or changing history. Import previews require explicit confirmation; conflicting drafts require a separate acknowledgment and retain their pins. Export includes saved inputs, assignments and absences, but excludes unsaved forms, repair previews and undo history. Nothing is automatically persisted or uploaded. Imported files may contain personal information.

The v0.3.1 patch handles a failed worker startup or initial message without leaving search permanently busy. It reports the failure, preserves the roster and undo history, and allows retry. Solver rules and the file format are unchanged.

Challenges, learning and credits

The difficult part was keeping solver correctness, reversible state and understandable interaction aligned. Stale-plan rejection, atomic capacity checks and keyboard-contained file previews showed why trustworthy repair needs more than a ranking formula. Making the reassignment chain visible also made it easier to distinguish a plausible proposal from a change the organizer had actually approved.

Original source, synthetic fixtures and custom icons were created for this project on October 25-28, 2026, by Sharon Basovich with Codex AI assistance in design, implementation and verification. Claude through Kiro provided bounded static review for v0.3.0. Original code is MIT-licensed; React and other dependencies retain their own licenses and notices.

Limits include no organizer study, real-data evaluation or measured impact; no multiuser or overnight scheduling; and no Safari, Firefox, physical-device or screen-reader testing. Applying a repair does not obtain volunteer agreement or send messages. It is not designed for employment, emergency or medical staffing.

Try the browser demo

Open RosterFair in your browser — no installation or account required. Record a demo cancellation, compare A and B, explicitly apply an option, then Undo. Try No complete repair to inspect a proven shortfall.

The host receives ordinary requests for the page and its static assets. Roster edits and repair calculations run in browser memory; the app does not upload roster files. Reloading resets unsaved edits. Use synthetic data for the public demo. The hosted source adds static browser packaging, a contrast-stable preview toggle, and explicit error/timeout recovery; the existing downloadable v0.3.1 remains available below.

Try the downloadable local demo

Download RosterFair v0.3.1 or visit the release page.

  1. Extract the whole ZIP. It contains the compiled app; GitHub's automatic source archives do not.
  2. With Node.js 22.14 or newer, run node server.mjs from the extracted folder. On Windows, you can also double-click Start-RosterFair.cmd.
  3. Open http://127.0.0.1:4173/. Select Record demo cancellation, then Find feasible repairs. Compare A and B, inspect a role, explicitly apply an option and try Undo.
  4. Try No complete repair and Contradictory pins to inspect the limits rather than an invented successful solution.

The compiled build needs no dependency installation, account or external runtime connection. This ZIP runs on your computer; the optional browser-hosted build above is separate. The v0.3.1 release addendum identifies the patch; some original package/README version labels remain v0.3.0 because the archive preserves the exact reviewed source.

Built With

Share this project:

Updates

Submission history