A personal, offline Python tool that allocates people to committees under explicit rules, produces a proposed full assignment, and explains why each assignment was made.
- Primary user: a single decision-maker
- Scale: ~60 people, ~25 committees, ~10 rules
- Output: a complete proposed allocation + per-seat rationales + committee health summaries
- Interaction model: run scenarios, optionally lock some choices, tweak rule weights, re-allocate, compare
- Create a virtual environment and install the package:
python3 -m venv .venv
source .venv/bin/activate
pip install -e .- Run the tests to verify the installation:
pytestSample CSV and YAML files are provided in the examples/ directory:
people.csv
name,service_cap,competencies
Alice,2,finance;strategy
Bob,1,
Carol,1,strategy
Dave,1,financecommittees.csv
name,min_size,max_size,required_competencies
Finance,1,2,finance
Strategy,1,2,strategy
Operations,1,1,rules.yaml
- name: service_cap
kind: hard
priority: 1
params: {}
explain_exclude: "{person.name} is at capacity"
- name: has_competency
kind: soft
priority: 2
weight: 1
params:
competency: finance
explain_score: "{person.name} has finance competency"
- name: has_competency
kind: soft
priority: 2
weight: 1
params:
competency: strategy
explain_score: "{person.name} has strategy competency"Run the allocator over the sample files:
python -m committee_manager.cli.main allocate --people examples/people.csv --committees examples/committees.csv --rules examples/rules.yaml --output output_dirThe command writes allocation.yaml and rationale.yaml to output_dir.
Run a minimal web interface that loads files from the inputs/ folder and
shows them for editing before allocation:
python -m committee_manager.web.appVisit http://localhost:5000/ in a browser to view the files in inputs/ and
generate an allocation without uploading.
- Offline, file-based (CSV + YAML inputs/outputs).
- Transparent: every seat has a rationale; every exclusion has a reason.
- Deterministic: same input = same output.
- Private: all local, no network calls.
Non-goals (MVP): meeting logistics, notifications, user management, databases.
Represents one shareholder in the pool.
Attributes
id: strname: strage: intsex: Literal["F","M","Other"]family_branch: strcompetencies: set[str]executive_role: boolconflicts: set[str]current_committees: set[str]service_cap: intcooling_off: dict[str, date]notes: str
Methods
workload() -> inthas_competency(comp: str) -> bool
Represents one committee to be filled.
Attributes
id: strname: strsize_min: intsize_max: intrequired_competencies: dict[str, int]desired_competencies: dict[str, int]hard_exclusions: set[str]diversity_targets: dictrotation_years: intcurrent_members: list[tuple[str, date, date]]locked_members: set[str]
Methods
current_coverage(assignees: set[Person]) -> CoverageSnapshot
- Hard constraints: if violated, person is ineligible for that committee.
- Soft preferences: add positive/negative points depending on how a candidate affects committee health.
Hard
- Minimum age per committee
- Executive exclusion
- No siblings on same committee
- Capacity cap
- Cooling-off period
Soft
6. Competency coverage
7. Branch diversity
8. Gender balance band
9. Tenure mix
10. Conflict overlap penalty
Each rule has:
name,kind (hard|soft),priority,weight(soft only),applies_to,params- Explanation templates:
explain_exclude,explain_score
- People
- Committees
- Rule set (YAML)
- Scenario (optional)
- Feasibility pre-check: eligible pool per committee, detect impossible cases.
- Greedy allocation: fill seats starting with hardest committees; pick highest marginal score.
- Local improvement pass: bounded swaps/drops to improve total health.
- Result packing: allocation + rationales + health summaries.
Per seat: