Skip to content

Planning poker

Verified against v0.10.0 · internal/poker/deck.go, internal/poker/stats.go, internal/poker/routes.go, internal/api/decks.go, web/src/components/DecksPanel.tsx, web/src/components/Table.tsx, web/src/lib/motion/plan.ts, web/src/pages/PokerRoom.tsx, web/src/lib/notifications.ts, web/src/components/NotificationSoundButton.tsx, web/src/lib/notificationAudio.ts

A poker session is a queue of work and a table of people. The facilitator puts one item on the table, everybody votes face-down, and the round opens when the facilitator calls it — or, if they opt in, when the last person votes.

Notification sounds are off by default. Turn them on from Your profile or the Sounds off button in a poker room; both controls save the same account preference. A rising two-note cue marks a selected or reset story, and a higher two-note cue marks a manual or automatic reveal. The cue still plays while the tab is in the background.

When a reveal sends a volley of emoji at the one dissenting voter, each throw makes a sound as it lands: food squashes, bread thumps, and everything else pops. Removing someone from the table thumps as the boot connects. Impact sounds follow the animation, so they are skipped under reduced motion and while the tab is in the background.

Browsers may require one interaction before allowing audio. If that happens, the preference remains on and the room offers Enable audio in this tab. Muted rooms keep following live state without replaying missed cues when sound is enabled again. Signed-link guests do not have this account setting.

Work goes in as either a ticket — with a reference like PLAT-412 — or an ad-hoc line item, which is what you want when someone says “and what about the migration script?” mid-session. Both take optional notes, and both can be reordered with the arrows.

The facilitator owns the queue: only they may create, edit, reorder, select, or delete a story. Ordinary members still cast and change their own votes. These permissions are enforced by the API as well as by the controls shown in the UI.

The reference is real data rather than decoration: it travels through the API and the WebSocket state and comes out in the CSV export as its own column, so a session’s output can be matched back to your tracker.

A ticket needs a reference or a title — either one on its own is enough, so “just estimate PLAT-412” is one field and a click. Titles are capped at 200 characters, notes at 2000, and a reference at 40.

A poker room with PLAT-412 on the table and its note beneath the title, and a queue of five items to the right — four ticket references and one ad-hoc line.A poker room with PLAT-412 on the table and its note beneath the title, and a queue of five items to the right — four ticket references and one ad-hoc line.
The item on the table carries its reference and its note; the queue behind it mixes tickets with the ad-hoc line somebody added mid-session.

Four built in, plus any a space has saved of its own, chosen when the session is created and fixed for its lifetime. Every deck also carries ? — “no idea” — and a coffee card. Both appear in the per-value counts, so you can see that three people said ?, but neither is ever fed into an average, a median, a mode or a range — nor into consensus: a round where everyone picked ?, or everyone picked coffee, is unanimous about nothing and the room is not told it agreed.

Fibonaccifibonacci
12358132134?

median, average, per-value counts

Modified Fibonaccimodified-fibonacci
0½12358132040100?

median, average, per-value counts

T-shirttshirt
XSSMLXL?

mode and range — no average, because there isn't one

Powers of twopowers-of-2
12481632?

median, average, per-value counts

A vote is validated against the session’s own deck on the way in, so a client cannot store a value the deck does not contain.

A space owner can save its own decks under Settings → Decks: a name, between two and fifteen cards, and a switch for whether the cards are an order rather than numbers. They then sit beside the built-in four in the create dialog, and every member of the space can see the list even though only an owner can change it.

A saved deck is a template, not a reference. Starting a session copies the cards into that session, so renaming a deck, rewriting its cards or deleting it outright leaves every room already dealing them exactly as it was — no vote already cast can be invalidated by an edit here.

The New session dialog with Poker selected, the title Sprint 35 planning typed in, and six deck cards to choose from — Fibonacci, Modified Fib, T-shirt and Powers of 2, then the space's own Platform sizing, selected, and Bug triage — above an unchecked Auto-reveal when everyone has voted.The New session dialog with Poker selected, the title Sprint 35 planning typed in, and six deck cards to choose from — Fibonacci, Modified Fib, T-shirt and Powers of 2, then the space's own Platform sizing, selected, and Bug triage — above an unchecked Auto-reveal when everyone has voted.
The deck chooser at session create: the four built in, then whatever this space has saved. The choice is fixed for the life of the session.

Nobody sees a value before the round opens. This is enforced where it has to be: the server builds a redacted view of the session for each viewer, and the WebSocket broadcasts that view — an unrevealed vote is structurally absent from the payload rather than present-but-hidden by the client.

By default the round stays closed until the facilitator hits Reveal, even when everyone has voted. That beat is for discussion before the cards flip. Teams that want the old “last vote opens the round” behaviour turn Auto-reveal on — at session create, or live in the room — and the check runs with the same rules as before.

When auto-reveal is on, the round opens once everyone who can vote has voted. Spectators are not in that denominator, and neither is anyone whose seat has gone away — otherwise “4 of 5” would hang forever because someone shut their laptop.

Sitting out ends the wait. Switching yourself to spectator mid-round takes you out of the set the round is waiting for, and if you were the last person it was waiting on, the round opens there and then — with open voting on, against the recorded list just the same. Sitting back down before the reveal puts you back in the set, and the round waits for your vote again — a vote you had already cast still counts. A vote you cast before sitting out is set aside while you are out, so it never holds the round open behind you: somebody else finishing, or sitting out themselves, still ends the round. Anyone who sits out rather than voting sees the estimates without having committed one, so a facilitator watching the room may want to say something about that.

The check otherwise runs only when a vote lands, or when the facilitator turns auto-reveal on mid-round with a full table — never on connect or disconnect. A disconnect can shrink the denominator; it can never open the round. Otherwise closing a laptop would be a way to reveal everyone else’s votes. Turning auto-reveal off mid-round does not close an already-open round.

Open voting, for a team that is not in the room

Section titled “Open voting, for a team that is not in the room”

A distributed team cannot all be at the table at once, and the denominator above is unforgiving about that: it counts who is connected, so a round cannot complete while half the team is asleep.

Open voting changes what the round waits for. When a story goes on the table with open voting on, Parley records who is expected to vote — everyone who has been in this room, whether or not they are here now, minus the spectators — and the round waits for exactly those people. Opening the room once is enough to be counted; you do not have to still be connected, and you do not have to have voted. Somebody who comes back hours later still finds the round waiting, and their vote is the one that completes it.

The list is the room, not the space. A forty-person space that runs five-person rounds gets a five-person round: the thirty-five who never opened this room are not in it and never hold it up. A link guest who has been in the room is in it on the same terms as anyone else — no members row required.

Because the list is a snapshot, it does not move under the round. Someone opening this room for the first time after the story went on the table is not in it: they are very welcome to vote, and their vote is a bonus that can never hold the round shut. Somebody who merely closed their tab is in it and is still waited for. And if a person in the list is removed from the space, or their guest link is revoked or expires, the round stops waiting for them — their vote could never arrive, and a round wedged on it would never complete.

Turning open voting on mid-round records the list for the story already on the table, so that round can complete too. If the story has already been revealed there is no round to record, so Reset takes the list instead — a re-vote on a revealed story waits for whoever is in the room at that point, latecomers included.

Open voting reveals nothing by itself. It only changes who the round is waiting for; whether the last vote opens the round is still auto-reveal’s question, and with auto-reveal off the facilitator still hits Reveal. The two are independent switches and all four combinations do what they say.

Either way the room names the seats it is still waiting for, so an open round that has stalled shows who it has stalled on.

Open voting sits beside auto-reveal on both surfaces: it is a checkbox in the create dialog — Open voting for people who are not in the room — and a switch in the room, next to the auto-reveal one, that only the facilitator sees. Turning it on or off there takes effect for the round on the table, without resetting it: switching it on widens the round to the people who are not in the room, and switching it off narrows it back to whoever is present, which is how a round stalled on somebody who has gone home gets moving again. The same change is available to scripts as PATCH /api/sessions/{id}/actions/config; see the API reference.

Four face-down cards at the table and two empty seats, with the line '4 / 6 voted' above them and the viewer's own 5 raised in their hand.Four face-down cards at the table and two empty seats, with the line '4 / 6 voted' above them and the viewer's own 5 raised in their hand.
Mid-round: four cards are down and the count reads 4 of 6. The other values are not hidden by the browser — the server strips them out of the payload, so there is nothing to peek at until the round opens.

Numeric decks report a median up front, an average underneath, and a count for each value. A median can land between two cards — 3 and 5 give 4, which is not a Fibonacci card — and an estimate has to be a card from the session’s deck, so the round shows that median without offering to save it. Re-run the round, or talk it through and vote again until the room lands on a card.

Ordinal decks do not. A T-shirt round reports the mode and the range — “mostly M, spread S to L” — because the average of S, M and L is not a size. An “average: 4.5” over T-shirt sizes is a correctness bug displayed to the whole team on a shared screen, and Parley declines to render one.

A revealed T-shirt round showing a large M under the heading MODE, the line 'range S–L · 6 votes · ordinal deck, no average', and per-value counts of S ×1, M ×4 and L ×1.A revealed T-shirt round showing a large M under the heading MODE, the line 'range S–L · 6 votes · ordinal deck, no average', and per-value counts of S ×1, M ×4 and L ×1.
An ordinal deck reports the mode and the range, spells out that it will not average, and offers to save M onto the story.

A unanimous reveal is celebrated: seats pair off across each row, crouch, jump and high-five at the top, with confetti at the point of contact. Anyone left without a partner jumps solo on the same beat. It waits for the last card to finish turning over, then runs for a little over a second — under three seconds all told, even at a full table — so it never delays the next round.

Everything here is decoration. prefers-reduced-motion: reduce skips it entirely — not just the movement, the confetti is never drawn — and the reveal, the statistics and the saved estimate are identical either way.

A reveal where every voter but one landed on the same card gets the opposite reaction: each of the other seats lobs an emoji across the table at the lone dissenter. Each one is a real throw: it arcs under gravity, bounces off the avatar it hits — glancing or square, depending on where it lands — and tumbles off the bottom of the screen. They arrive on uneven beats, so it reads as a pile-on rather than a volley. The set is playfully cross — eye-rolls, red flags, and the classic theatre-heckling repertoire of tomatoes, eggs and a lemon — and never mocking, because nobody opted into being the target.

It is deliberately narrow. It does not fire at unanimity (that is the high-five above), nor with two or more dissenters, nor when the majority card is ? or coffee — the same exclusion that keeps those two out of consensus — and not at all below four voters, where “all but one agree” is true of nearly every split. The two celebrations can never both run.

Like the high-five it is pure decoration, and prefers-reduced-motion: reduce skips it entirely: no emoji is drawn at all.

A seat that appears mid-meeting drops into its slot and takes one light bounce. The row opens first — the seats already on the table slide to their new positions, and only then does the new one fall into the gap they made, rather than landing on a row still rearranging itself under it.

Several people arriving at once — the usual start of a standup — are staggered across a fixed budget rather than a fixed gap each, so a dozen joiners are all on the table inside half a second instead of queueing up one after another. A reconnection is not an arrival: coming back from a dropped network does not replay the whole room as though everybody had just walked in. Someone who genuinely leaves and comes back does drop in again.

prefers-reduced-motion: reduce skips it entirely — the seat is simply there, with no movement and nothing scheduled.

The facilitator has a Hand off control in the room header. It opens the roster; picking a name moves the role straight away — no grace period, no accept step — and the room is told who is driving now.

That is the deliberate path. The other one is the claim: if the facilitator loses their connection, a banner counts out a 60-second grace period, after which anyone at the table can take the chair. A guest joining through a signed link is offered neither, and cannot be handed the role.

The facilitator can remove a participant from the room they are running: hover a seat at the table and a small × appears on it. Only the facilitator sees it, it never appears on their own seat, and it goes away once the session has ended. Confirming asks for an optional message before it fires. It ends that person’s seat in this meeting and nothing else: their sockets for the room close and the seat they were occupying at the table empties on the next update. They are still a member of the space, so they still appear wherever the space’s people are listed, and they can walk back in through the same link or room code.

If a round is open when they go, it stops waiting for them: a round that needed their estimate to auto-reveal reveals as soon as everyone still at the table has voted. A vote they had already cast stays on the table and still counts towards the result — removing someone ends their seat, it does not retract what they had already said.

The removal can carry a short message — “we will pick this up tomorrow” — which the person sees on the way out. The box stops at 80 characters, which is what fits the connection’s closing frame once accents and emoji are counted.

The person removed lands on a screen of their own, with the message if there was one. It is not the “you no longer have access to this space” screen, because that is not what happened — they still belong to the space, and the way back in is the same link they came through.

Everyone still at the table watches a boot swing in from beside the seat, connect, and launch it off the screen; the row closes up only once the seat has fully gone. The boot is a pendulum rather than a canned slide, so it dips on the way across and is on its way back up when it lands — which is why the seat leaves sideways instead of straight up, and why a seat on the far side of the table takes a little longer to see off. If you have asked your system for reduced motion, the seat simply goes.

It is deliberately the small hammer. Removing somebody from the space itself is a separate, owner-only action on the space settings page, and there is no per-room block list: a removal is reversible, and stopping someone coming back means taking away their way in.

After the reveal the facilitator can save the agreed value onto the story with one click, which writes the estimate and moves the queue along. If a round has no saveable result — everyone voted ?, or the deck is ordinal and there is no single winner — the save control does not appear rather than inventing something to write.

Reset clears the votes on the current story and starts it again.

Ending the session keeps the results in the space. Anything in the queue that never received an estimate — including a story left mid-vote — is listed on the ended panel, in queue order, so the leftover is a backlog rather than a memory. A session that estimated every story says so, instead of showing an empty list.

CapabilityStatusWhat to do instead
Vote timerNot builtUse a facilitator's own timer. Opt-in auto-reveal can end a round without one when everyone has voted.
Estimate history across sessionsNot builtExport each session to CSV and keep them; cross-session analytics is on the roadmap.
Sharing a deck between spacesNot builtA saved deck belongs to one space. Copy the cards into a deck of the same name in the other space; an org-owned deck is on the roadmap.
Editing a revealed voteOut of scopeReset the round instead — silently changing a revealed vote would make the reveal meaningless.