How to run BNGZR scoring
Everything here happens in the control sheet. You never need GitHub, a terminal, or anything else. Press a menu item and the engine does the rest.
The one rule: scores are immutable, everything else is not. The engine recomputes the entire season from scratch on every run, so a correction to the roster reaches every day it should have. You cannot "break" a score by editing the roster — fix the roster and run again.
Before your first time
The first time you use the BNGZR menu, Google will ask you to authorise the script. The screen is blunt and looks like a security warning — it is the normal consent prompt for a script that lives in this sheet. Approve it once and you will not see it again.
Part one — everyday tasks
Import a day's scores
- File → Import, choose the capture CSV
- Import location: Insert new sheet(s). Never "Replace spreadsheet" — that wipes the entire workbook, roster and all. If it ever happens, File → Version history puts it back
- Keep the tab name as it is. The date lives in that name and the engine reads it. It needs to contain either a full stamp like
2026-09-01T08-00-00or just2026-09-01. A tab named "June 17 scores" carries no date the engine can read and is skipped in silence - BNGZR → Run Engine
The engine finds the tab, works out the data date (the capture timestamp minus one day, because the cut closes out the previous day), stores the scores, recomputes the season and republishes the site.
Check it worked: open the report site's Daily scores page and confirm the new date is there.
Do not rely on the Activity Log line or the "Computed at" time for this. Both of those record that the button was pressed, not that your day was read — so a tab the engine could not date will still produce a fresh timestamp and a log entry while importing nothing at all.
Importing the same day twice is safe — the engine replaces that day whole rather than adding to it, so nothing doubles.
Never delete or edit an imported capture tab. Those tabs are the season's record. They are re-read on every run, so editing a score changes the standings, and deleting a tab can take that day's scores with it. Tidy the sheet at the end of a season, not during one.
Before the season starts every run will say "No column for …". That is expected and harmless — the scores still import, only the roster stamping is skipped.
What Run Engine actually does
Two separate things, and only one of them is global.
- It stamps today's column on
Roster History— in **the book you pressed it in, and only that one**. Everybody's current union is carried into today, and anyone whoseStatusreadsGoneorMovedgets a-instead. - It tells GitHub to rebuild everything. That half does not know or care which book you pressed in — the button sends no sheet name at all. Both seasons are rescored and the whole website is republished either way.
So: does it matter which sheet I press it in? Not for the reports. Yes for the stamping. If you mark somebody Gone in one book, press Run Engine from that book so their - actually lands there.
Where you paste the capture is a different question with a simpler answer: each season reads capture tabs only from its own control sheet. Summer's go in the Summer sheet, Fall's go in the Fall sheet. Nothing crosses over.
Falling behind, and catching up
There is no deadline on any of this. The date lives in the tab name, not in the clock. The engine reads the date out of the name you imported and files the scores under the day they belong to, so importing Tuesday's export on Thursday still puts it on Tuesday.
All of these are fine:
- Doing today's import tomorrow
- Doing today's import tomorrow, then tomorrow's an hour later — two tabs, one press
- Skipping three days and importing the lot in one sitting, in any order
Nothing is lost by falling behind. The SP in the export is a running total, so whatever built up while nobody was looking arrives with the next import. Tested against a real season with three days removed: the season totals came out identical to the point, and the ranked list was identical top to bottom.
Two things do look odd until you catch up, and both fix themselves:
- The days you skipped read 0 on the Daily scores page, and the next day you imported
looks enormous — because it is carrying all of them at once
- The season average reads high, because it divides by the days it actually has. It
lifts everybody by the same amount, which is why the standings do not move
To catch up: import the tabs you missed, then press Run Engine once. That is the whole procedure. Order does not matter — every press recomputes the entire season from scratch — and it only reads tabs newer than what it already has, so a batch costs one pass.
The two things to watch
Keep the tab name. It is the only thing carrying the date. A tab renamed to something like "Tuesday scores" is skipped in silence — the run still reports success and the site still restamps, so nothing tells you the day did not land. Confirm on the Daily scores page, not by the timestamp.
Someone leaving is the one thing that is time-sensitive. Pressing Run Engine stamps the roster into today's column. If somebody's Status goes to Gone on the 19th and nobody presses until the 21st, their - lands on the 21st and they will have scored two days they should not have. Fix it the same way as any other past-day correction, below — set the Status, then put - in the right day's cell yourself.
If the export was never taken for a day, that day's snapshot does not exist anywhere and cannot be rebuilt. Totals and standings are still correct; you just will not be able to see how that one day broke down. If you have the file, import it late and the day comes back properly.
Check the system is alive
Look at the site footer: Computed at …. If that time is advancing when people press Run Engine, everything is working. If it is stuck, see Troubleshooting.
The Activity Log tab lists recent actions. It is a glance, not an audit — it keeps the last few hundred lines.
Part two — roster changes
All of these are edits to Roster History, which is a grid: one row per person or pool, one column per date, CU showing where they are now.
Move somebody, starting today
Type the new union number in today's column on their row. That is the whole task.
Every following day inherits it automatically — the next Run Engine stamps today's value forward, and keeps doing so until somebody types something different.
Correct a past day — and why it needs two steps
Say somebody moved on the 14th and it was typed wrong. Fix the cell on the 14th and you are not finished, because every day after it still holds the old value:
13 14 15 16 17
4 2 4 4 4 <- corrected the 14th only. Still wrong.
CU reads the last column, so they still show union 4 and the fix silently reverts the next day. It looks done and isn't.
So: fix the cell, then BNGZR → Fill Forward from selection. Select the corrected cell — a player's row, never the header row of dates — run it, and the value pushes across to today. It asks first and tells you how many columns it will cover.
Then BNGZR → Run Engine, or the reports keep showing the old history until somebody else happens to run it.
You can also drag-fill by hand if you prefer — the menu item just makes it one deliberate action instead of one you can forget halfway.
Add somebody
Rostertab — new row: PFID, in-game name, Pool ID if they are joining a pool, StatusActiveRoster Historytab — new row with the same PFID in column A and their name in column B- Put their union in today's column
They score from the first day they hold a union. Nothing before that.
They will not appear in any report until a capture actually shows them scoring — a brand-new player with a union but no points yet is simply not on the board.
Somebody leaves
Set their Status on the Roster tab to Gone (left entirely) or Moved (went to another union). The next Run Engine writes - into today's column for them and carries it forward. From that day they stop scoring, and on the P/R board they show as unplaced with no proposal — visible, but out of the promotion ladder. Verified: a player marked out mid-season keeps every point earned before that day and gains nothing after it.
One catch on timing: that only works if today's column is still empty for them. If Run Engine has already been pressed today, their union is already stamped and - will not land until tomorrow — so they would still score today. If in doubt, type - in today's column yourself as well. Doing both is harmless.
The engine reads -, OUT, GONE, MOVED and 0 as the same thing, so older cells reading OUT are still correct and typing the word still works.
Somebody comes back
Two things, and both are needed:
- Set their Status back to
Activeon theRostertab - Type their union in today's column on
Roster History
Leave the Status on Gone and the union you typed survives today and is overwritten with OUT tomorrow — the silent revert this document warns about elsewhere, wearing a different hat.
Hold somebody
Put Y in the Hold column on Roster (or on Pools for a pool).
A hold blocks promotion but not relegation. A held player who ranks high enough to go up simply stays put, and the P/R board says so in its Reason column rather than quietly showing "stay".
Pools
A pool is a placeholder holding one roster spot for several real accounts. The pool is what appears in every report; its members never appear as themselves.
Poolstab: the Pool ID and display name, plus Members and Who — two formulas
that list the pool's current members. They update as you type; no run needed
Rostertab: put that Pool ID on each member's rowRoster History: the pool has its own row and its own union. Members do not need one
Who showing an em dash means the pool has no members at all, so it scores nothing. A #number instead of a name means that Roster row has no In-Game Name filled in — the member still counts, you just cannot see who it is until you add the name.
Pools cannot be created or changed after week two. That is a union rule; nothing in the software enforces it, which is exactly why it matters that people follow it.
Never change a member's Pool ID mid-season. Pool membership is applied to that player's whole season, not from the day you change it — so editing a Pool ID silently re-attributes every past day of their scoring to the new pool.
Part three — promotion / relegation
P/R happens whenever you have time. Friday, Sunday night, the Tuesday after, or not for a month. Nothing is scheduled and skipping is not a special case — it is simply nobody pressing anything.
Running a shuffle
- Decide how much movement you want.
Configtab,pr_edge: -3— only the best three and worst three of each union may move. Conservative -All— rank everyone and refill the unions in order. Churns more - Use the dropdown. A typo is read asAll, which is the least conservative setting — the safe-looking mistake fails in the churny direction - After a long gap, open it up for one catch-up shuffle, then put it back P/R Proposaltab, cell B1 →DRAFT- BNGZR → Run Engine. The board fills in: everyone ranked, where the rule would put them, and why.
Decisionis deliberately blank — the engine never pre-approves itself - Set B1 →
REVIEWING. From here the engine will not touch the tab. Take as long as you like - Fill in
Decisionon each row you want to act on: -Approve— take the proposal -Keep, or leave blank — leave them where they are. Blank always means Keep -Custom— put the union you actually want inFinal Union. Custom with an emptyFinal Unionmoves nobody, and Apply will name those rows rather than skip them - Set B1 →
APPROVED - BNGZR → Apply P/R. It shows you the moves and asks. Confirm, and it writes them into today's column, sets B1 to
APPLIED, and stamps who did it
If Apply refuses
"the board is stale" means the roster changed after the board was drawn — the moves were worked out against a roster that no longer exists. It names the rows that drifted. Run Engine to redraw the board, review again, and re-approve. Nothing was written.
Undoing a move
Edit Roster History like any other correction — fix the cell, then Fill Forward. The board has no special authority once applied; the grid is the record.
Potential Moves
The site's Potential Moves page updates every run and shows where the standings currently point, unrestricted. It is an early warning, not a decision — nothing there has been agreed by anyone.
Part four — troubleshooting
The site's "Computed at" is not advancing. A run failed. The Activity Log will show whether the engine was even dispatched. Tell Wizdumb.
Run Engine says "engine not dispatched". The GitHub connection is not configured. Wizdumb's side.
Run Engine says "No column for <date>". Today is outside the season, or the date columns need extending. Before the season starts this is normal and expected — the run still happens, only the stamping is skipped.
A score looks wrong. Scores come from the capture. They can be edited, because a capture tab is an ordinary tab — but don't: every run re-reads those tabs, so an edit there changes the standings and there is no record of what the game actually reported. What is almost always wrong is where somebody was: check their row in Roster History for the days in question, fix it, Fill Forward, Run Engine.
Somebody is missing from the reports. They need four things: a row on Roster, a row on Roster History, a union on the days concerned, and points in a capture. A player who is correctly set up but has not scored yet simply is not there.
A pool member is showing as themselves. Their Pool ID is missing on the Roster tab.
Two people edited at once. Harmless. The engine reads the sheet once at the start of a run; whoever saved last wins, same as any spreadsheet.
Part five — Wizdumb only
These are the switchboard tasks. Ruby never needs them.
Before the season starts
- Place everyone. Every player and pool needs a union in the season's first column, or they will not score. Anyone genuinely not playing should read
- - Check the pools — every pool needs a union of its own
- Point the engine at the real season. The reports show a loud
PRACTICE DATAbanner and the sheet's mirror tabs are named(PRACTICE)until this is switched. When it is, both change on their own - Protect the record. Lock the dated columns of
Roster History, plusRoster,Pools,Configand the imported capture tabs, excluding the engine's service account. This is what makes it impossible for the engine to rewrite history rather than merely unlikely. Do this once the sheet's shape is settled — it locks setup tooling out too - Share the sheet — management team as Viewers, Wizdumb and Ruby as Editors. Viewers see everything and can change nothing
The Summer shadow sheet — until 2026-08-23
There is a second sheet running the tail of the Summer season through the new system. Its job is not to produce Summer's numbers — the old workbook is still doing that. Its job is to let you practise the exact routine you will use in Fall, on real days, while the old workbook is still there to check the answer against.
So do it the Fall way, in the shadow sheet, and do not touch the live workbook for it:
- File > Import the capture into the shadow sheet —
Insert new sheet(s), and keep the tab name Google gives it (ts2-all-unions-2026-08-21T...). That name is the whole Fall convention: the engine takes the capture time from it and files the scores against the day before. No renaming, no editing. - Run Engine, in the shadow sheet.
That is it. That is also, precisely, what you will do every day in Fall — which is the point of the exercise.
The engine does not read the live workbook for scores. It used to, and that was wrong for practice: importing there forces the old Summer habit of renaming the tab to the day before, and that habit disappears on 2026-08-31. Each season now takes capture tabs only from its own control sheet. A capture pasted into the live workbook will not reach the engine.
The live workbook is still read — but only to mark your homework. Two steps compare the engine's answer against it every run, and neither can write to it or change what the engine scored. Where they disagree, the workbook wins until the season ends.
Being behind on the workbook is fine. The comparison stops at the workbook's own last populated day and says so — workbook : has no scores past 2026-08-16; the engine is 2 day(s) ahead. It is a note, not a failure; it never stops the report being published. So if the last week of Summer is just housekeeping, leave it and let the shadow run ahead.
Results appear at /summer/ on the reports site under an orange SHADOW RUN banner. The numbers there are real — just not the record.
Worth doing once, early: run a real P/R shuffle there, start to finish. It is the one part of the system nobody has done on live data, and after 2026-08-23 the next chance is the real season.
One thing to know: the shadow sheet's roster is a copy, taken on 2026-08-18. It does not follow edits you make to the live workbook, and vice versa. If somebody moves union or leaves during the last week, make the change in the shadow sheet too — and press Run Engine there, because the stamping only ever touches the book you press it in. The run reports exactly which rows disagree, so you are not hunting.
After 2026-08-23 Wizdumb switches it off (one repo variable) and the /summer/ pages come down on the next run.
Tabs the engine owns
P/R Proposal, Activity Log, Standings, Daily Scores, Preview. All rewritten on every run. Do not hand-edit them; anything typed there is lost on the next press.
Roster, Pools, Roster History, Config are yours. The engine only ever reads them. The Members and Who columns on Pools are the exception the engine cannot touch: it reads that tab by position and only ever looks at columns A–D.
The Preview tab
Standings is a photograph — it shows what the engine computed at the last press and does not move until the next one. Preview is the live version: change a Pool ID on the Roster and the pool's member count, total, score and rank all move as you type.
Read the two right-hand columns together:
- Last run — the rank the engine gave this subject at the last press
- Vs run — how far your edits have moved it since. Negative means it fell
So a pool dropping from 12th to 40th reads as Last run 12, Vs run -28, immediately, before you press anything.
Three things it is not:
- It is one-way. Nothing on it feeds the engine, the ledger, the P/R board or the site.
The published report always comes from the engine
- It shows standings only — never P/R moves. Promotion, relegation, holds and the
edge rule stay in the engine
- It is exact for a season of 84 days or fewer, which covers Summer entirely. Over the
last week of a full 91-day season it drifts slightly. Vs run is where you would see it
Somebody added to the Roster since the last press is not on Preview yet — the tab is drawn with one row per subject at the moment it is written. Press Run Engine and they appear. Edits to people already there are live.
On a fresh run with nothing edited since, every Vs run should read 0.
The numbers still only become real when you press Run Engine.