Skip to content

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

WheelSpinner

A small Go CLI for picking a person at random from a team, like spinning a wheel of names. Designed for recurring team scheduling questions (who runs today's standup, who reviews the next PR, etc.) with built-in escalating "punishments" for repeat absentees.

Quick start

Requirements

  • Go 1.26+

Build and run

# build
go build

# run the binary
./WheelSpinner

# or skip the build step and run directly
go run .

The app keeps its state in a SQLite file called wheel.db in the working directory. The file is created and the schema migrated on first start, so there is no setup step.

What it does

Everything is scoped to a team. A team has:

  • a unique name,
  • a roster of people,
  • a cadence that controls how often the wheel may be spun (daily, workday (Mon–Fri only), weekly, monthly),
  • optional punishment ratios — a list of decimal multipliers used to escalate the weight of repeat absentees.

When you start the app it lists existing teams and lets you pick one or create a new one. Once a team is selected, the main menu is:

  1. Spin the wheel — runs a single spin for the team's current cadence period, with the cascading punish flow described below.
  2. View spin history — the team's finalized spins, most recent first. Drilling into a spin shows its winner, the recorded criminals, and the section weights at the time of the spin.
  3. View future spins — a table of every team member's projected weight for the next N spins, where N is the longest active punishment schedule. Useful for eyeballing who is currently being escalated.
  4. Wipe all data — destructively deletes every team, person, spin, section and criminal record. Requires typing yes to confirm. Table schemas and migration history are left intact.
  5. Quit.

Spin rules

Cadence

A team can only record one finalized spin per cadence period:

  • daily — one spin per calendar day.
  • workday — same as daily, but spins on Saturday/Sunday are refused.
  • weekly — one spin per ISO week (Monday–Sunday). A Friday spin and the following Monday spin are different periods.
  • monthly — one spin per calendar month.

If you try to spin again within the same period, the app prints when the last spin happened and refuses.

Section weights

Every member of the team is on the wheel for every spin. Each section's weight is the head of that person's punishment schedule — a per-person list of decimal multipliers that burns down by one position after each finalized spin. When the list is empty, the weight is simply 1.

The wheel animation draws each section sized proportionally to its weight, so a person currently under heavy punishment occupies a visibly larger slice and is more likely to be picked.

After the wheel lands

Once the pointer stops, you choose:

  • Accept winner — finalize the spin with this winner. Every team member's schedule advances by one position.
  • Respin — re-run the animation. Nothing is written to the database.
  • Punish criminal — see below. Only offered when the team has at least one punishment ratio configured.

The criminal / punishment flow

A person is only punished when the wheel lands on them while they are not present. Punishment is never retroactive: if they were in the room when the wheel picked someone else, they aren't a criminal for that spin, even if their weight was high.

When the wheel lands on an absent person, choose Punish criminal. The flow is:

  1. Their name is removed from the wheel for this spin (only in memory; the spin's stored sections still reflect the original lineup).
  2. The wheel is re-spun on the smaller wheel to pick a new winner.
  3. You can keep punishing — the cascade continues until a present person wins (or the wheel would be empty, in which case Punish is refused).
  4. When you finally Accept the surviving winner:
    • The spin row is finalized with that winner.
    • Each name you marked as a criminal is recorded in the criminals table for this spin.
    • Each criminal's punishment schedule is updated by stacking the team's ratios onto it (formula below).
    • Every non-criminal team member's schedule advances by one position.

Until you Accept, nothing is written to the database, so respinning and punishing are free of side effects.

How ratios stack

Given the team's ratios r = [r₀, r₁, … rₙ₋₁] and a person's existing schedule s = [s₀, s₁, … sₘ₋₁], applying a punishment produces a new schedule:

new[i] = sᵢ · r_(i mod n)        for 0 ≤ i < m
new = new ++ r                   (the ratios are appended)

So every existing entry is amplified by the corresponding ratio (cycling through the ratios if the schedule is longer than n), and a fresh copy of the ratios is appended to the tail.

Worked example

Ratios 3, 2, 1.5. A person is punished in consecutive spins, accepting no other spin in between (so nothing burns the schedule down):

punishments so far schedule (index 0 = next spin's weight)
0 []   (weight = 1)
1 [3, 2, 1.5]
2 [9, 4, 2.25, 3, 2, 1.5]
3 [27, 8, 3.375, 9, 4, 2.25, 3, 2, 1.5]

So after two punishments their next spin has weight 9, the one after 4, then 2.25, 3, 2, 1.5, and finally back to the default 1.

Storage and migrations

A single SQLite file (wheel.db) with these tables:

  • teams(id, name UNIQUE, cadence, punishment_ratios)
  • people(id, team_id → teams, name, punishment_schedule) with UNIQUE (team_id, name)
  • spins(id, team_id → teams, status pending|done, spun_at, winner_id → people)
  • sections(id, spin_id → spins, person_id → people, weight) with UNIQUE (spin_id, person_id)
  • criminals(id, spin_id → spins, person_id → people) with UNIQUE (spin_id, person_id)

Schema migrations live in migrations/, are bundled into the binary via go:embed, and are applied automatically on startup using pressly/goose.

To run migrations manually:

go run github.com/pressly/goose/v3/cmd/goose@v3.27.1 -dir migrations sqlite3 ./wheel.db status
go run github.com/pressly/goose/v3/cmd/goose@v3.27.1 -dir migrations sqlite3 ./wheel.db up
go run github.com/pressly/goose/v3/cmd/goose@v3.27.1 -dir migrations sqlite3 ./wheel.db down

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages