SHADOW RUN — THE SHEET IS STILL THE RECORDReal scores, computed by the new engine from the live capture tabs, running alongside the workbook. Where the two disagree, the workbook wins until the season ends.

BNGZRScore Tracking

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

  1. File → Import, choose the capture CSV
  2. 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
  3. 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-00 or just 2026-09-01. A tab named "June 17 scores" carries no date the engine can read and is skipped in silence
  4. 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.

  1. 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 whose Status reads Gone or Moved gets a - instead.
  2. 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:

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:

looks enormous — because it is carrying all of them at once

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

  1. Roster tab — new row: PFID, in-game name, Pool ID if they are joining a pool, Status Active
  2. Roster History tab — new row with the same PFID in column A and their name in column B
  3. 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:

  1. Set their Status back to Active on the Roster tab
  2. 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.

that list the pool's current members. They update as you type; no run needed

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

  1. Decide how much movement you want. Config tab, 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 as All, 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
  2. P/R Proposal tab, cell B1 → DRAFT
  3. BNGZR → Run Engine. The board fills in: everyone ranked, where the rule would put them, and why. Decision is deliberately blank — the engine never pre-approves itself
  4. Set B1 → REVIEWING. From here the engine will not touch the tab. Take as long as you like
  5. Fill in Decision on 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 in Final Union. Custom with an empty Final Union moves nobody, and Apply will name those rows rather than skip them
  6. Set B1 → APPROVED
  7. 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

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:

  1. File > Import the capture into the shadow sheetInsert 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.
  2. 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:

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:

The published report always comes from the engine

edge rule stay in the engine

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.