Skip to content

Repository files navigation

RosterFair verified assignments

RosterFair

CI CodeQL Release License: MIT Zero runtime dependencies

JSON in. Fair, explainable groups out.

RosterFair is a local-first CLI and JavaScript library for assigning people to tables, teams, classrooms, workshop rotations, volunteer shifts, or any other capacity-limited groups. It verifies every hard rule, optimizes weighted preferences, remembers past pairings, and produces JSON, CSV, or a self-contained visual report.

It is not another random-name shuffler:

  • hard constraints are either satisfied or the command fails;
  • a search-budget timeout is never mislabeled as proof of impossibility;
  • impossible inputs get specific repairs and an irreducible conflict set;
  • a seed reproduces the same result byte for byte;
  • multi-round mode penalizes repeated pairings across history and newly generated rounds;
  • all solving and reporting happen locally with zero runtime dependencies.

Try it in two minutes

Requires Node.js 20 or newer.

git clone https://github.com/KanadeK/rosterfair.git
cd rosterfair
npm ci
node ./bin/rosterfair.js demo workshop --out workshop.html

Open workshop.html. It contains the full report and makes no network requests.

To install the signed-off release package:

npm install -g https://github.com/KanadeK/rosterfair/releases/download/v0.1.0/rosterfair-0.1.0.tgz
rosterfair demo classroom --out classroom.html

Verify the archive first with the SHA256SUMS.txt attached to the same GitHub Release.

One input, several real settings

The included examples are deliberately not variants of one demo:

Scenario What RosterFair must handle
Classroom tables fixed/allowed positions, separated pairs, support caps, score balance, language spread
Wedding tables couples, accessibility, social conflicts, children per table, family-side mixing
Workshop rotations three rounds, prior-session history, company diversity, experience balance
Community kitchen shift leads, first-timer caps, pace balance, preferred assignments
rosterfair solve examples/wedding.json --format html --out wedding.html
rosterfair solve examples/workshop.json --rounds 4 --seed autumn --format csv
rosterfair check examples/classroom.json
rosterfair explain examples/impossible.json

Scenario format

Here is a complete small input:

{
  "$schema": "./schema/rosterfair.schema.json",
  "version": 1,
  "name": "Project teams",
  "seed": "demo-2026",
  "participants": [
    { "id": "ava", "name": "Ava", "attributes": { "experience": 8 } },
    { "id": "bo", "name": "Bo", "attributes": { "experience": 3 } },
    { "id": "chen", "name": "Chen", "attributes": { "experience": 6 } },
    { "id": "dara", "name": "Dara", "attributes": { "experience": 5 } }
  ],
  "groups": [
    { "id": "alpha", "name": "Alpha", "capacity": 2 },
    { "id": "beta", "name": "Beta", "capacity": 2 }
  ],
  "constraints": [
    { "id": "separate", "type": "apart", "members": ["ava", "bo"] },
    { "id": "balance", "type": "balance", "attribute": "experience", "weight": 4 }
  ]
}

Hard rules:

Type Meaning
together Every listed participant must share one group
apart Every pair in the list must use different groups
fixed One participant must use one group
allowed One participant may use only a listed group
attribute-limit At most N matching participants per selected group

Weighted preferences:

Type Meaning
prefer-together / prefer-apart Penalize an unwanted social arrangement
prefer-group Penalize assignment outside preferred groups
balance Bring numeric group averages closer together
spread Distribute categorical values across groups

See the schema guide and the machine-readable JSON Schema for every field.

Commands and outputs

rosterfair solve <scenario.json> [--rounds N] [--seed TEXT]
                  [--iterations N] [--max-search-nodes N]
                  [--format json|csv|html] [--out PATH]
rosterfair check <scenario.json>
rosterfair explain <scenario.json> [--json]
rosterfair demo [wedding|classroom|workshop|volunteers] [--out PATH]
  • JSON preserves scores, diagnostics, seeds, metrics, and all rounds.
  • CSV creates one row per participant per round for spreadsheets and imports.
  • HTML creates an offline visual report with round switching, printing, and JSON download.

The program writes machine-readable output to stdout unless --out is supplied. Diagnostics go to stderr, so pipelines do not receive mixed data.

Failure means something

Exit Meaning Repair
0 solved or hard rules are feasible keep the seed with the result
1 unexpected I/O/runtime failure inspect stderr and retry after fixing the environment
2 invalid JSON, schema, reference, or CLI usage fix every path reported by validation
3 hard constraints were proven impossible run rosterfair explain; change one named rule or capacity
4 search budget ended before a proof raise options.maxSearchNodes; do not treat this as “no solution”

An intentionally impossible fixture is committed:

node ./bin/rosterfair.js explain examples/impossible.json
# exits 3 and explains that a together block of 3 cannot fit capacity 2

The complete repair playbook is in docs/troubleshooting.md.

How trustworthy is the answer?

RosterFair separates two jobs:

  1. A bounded backtracking search enforces capacities and every hard rule. A returned assignment is rechecked before scoring.
  2. Deterministic local search improves soft preferences without being allowed to break a hard rule.

Hard-rule correctness is exact for returned results. Soft optimization is heuristic: the score is transparent and reproducible, but RosterFair does not claim a global optimum. See docs/algorithm.md for the model, repeat-pair accounting, complexity, and limits.

Acceptance commands

These commands exercise source syntax, unit and CLI tests, real examples, failure paths, and release packaging:

npm ci
npm run verify
npm run test:coverage
npm run pack:release
npm run release:check

Expected evidence:

  • all tests pass with no skips;
  • four feasible fixtures solve and render JSON/CSV/HTML;
  • examples/impossible.json fails for the expected reason;
  • release/rosterfair-0.1.0.tgz installs without downloading runtime packages;
  • release/SHA256SUMS.txt matches every published asset;
  • release preflight finds a clean tree, matching versions, no common secret pattern, and no co-author trailer.

CI repeats the verification matrix on Linux, Windows, and macOS. The packaging job uploads the exact release candidates as a workflow artifact.

Privacy and scope

RosterFair has no telemetry, account, hosted backend, or runtime dependency. Names and sensitive attributes never leave the machine unless you publish or send an output yourself. Reports intentionally embed the assignment data so they remain portable; treat the HTML file with the same care as the source roster.

This project allocates people according to rules supplied by its operator. It cannot decide whether those rules are ethical, lawful, accessible, or appropriate. Review sensitive attributes and final assignments with the affected people.

Project rationale

The selection research, rejected crowded ideas, and competitive gap are recorded in docs/project-rationale.md. RosterFair focuses on a missing layer—verifiable, reusable assignment logic and conflict explanations—rather than cloning a wedding planner or classroom canvas.

Contributing

Read CONTRIBUTING.md before opening a pull request. Reports of security or privacy issues should follow SECURITY.md.

MIT © 2026 KanadeK

About

Explainable, reproducible group and seating assignments from plain JSON.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages