# Changelog ## Unreleased - Deploy and watch a hosted app safely. `tools/deploy_host.sh` deploys only a commit with green gate checks that contains the running commit, unless `--rollback-to` is given, and never one with an older data schema than the live data. The image carries its schema as a label. It tests its cold backup with `gzip -t`, rolls back to the old image when the new one fails its health or smoke check, and keeps the 5 newest backups and their images. The alert watch hands over one "app down" alert when the host, the container or the alerts file cannot be read, and waits while a deploy holds its lock. The nightly watch runs the private-save tests at the deployed commit and checks the app with the smoke check, which works under accounts. Its systemd units live in `deploy/systemd`, run from the watch's own clone, post a failed run to ntfy and install with `tools/install_watch.sh`. The watchdog also warns when the data folder has under 10 GB free. - Rerun the failed jobs of a failed push run on `main` once, so one flaky job does not leave a commit without green checks. `tools/flake_rate.py` reports how many runs passed only on that rerun. - Name development builds from `main` as such. `main` is now 0.7.0.dev0, and `tools/release_version.py` accepts an `X.Y.Z.devN` version. It moves only the package version, so the installers and Compose files keep installing the published release. - Require green checks before a change lands on `main` or a `feature/` branch. CI and the Python install workflow each end in one gate job, `ci gate` and `install gate`, that passes only when every other job passed. The branch ruleset requires both, so `feature/` branches now take changes through a pull request. Pushes to `feature/` branches run both workflows, so each commit there has its own evidence, and a documentation-only pull request runs the install checks too. The release gate requires both gates. - Stop two linked games trading the same species back and forth. Each copy scored a small gain, and the receiver's PC released it at the next visit. Before an automatic trade, each receiver now rates the incoming Pokémon with its own PC rules, and a trade one of them would release is skipped. A Pokémon that completes a Pokédex entry is always sent. After a trade or a refusal, the same two games leave that species pair alone for 6 hours. - Send a Gold, Silver or Crystal Pokémon to heal once its strong attacks run out, even when a weak one has PP left. A level 84 Typhlosion carried Kanto with Flamethrower and Strength, and when both were spent, 30 PP of Cut kept it from the Pokémon Center. It met Misty and Brock with Cut and Swift alone, fainted, and the weak rest of the party could not finish the battle. The heal walk now counts only attacks that hit at least half as hard as the Pokémon's strongest. - Skip a Gold, Silver or Crystal event return or gift the PC would release. Gold caught the returning Sudowoodo and released it 11 seconds later, and took the Eevee gift only to release it. A due return or gift the PC would rate as a spare now waits for the next walking milestone, and the journal says so once. The loop guard counts a caught return or completed gift as progress only once it has stayed in the PC for a check. - Stop League reward gifts from looping through the PC. Since PC values, a reward copy counts as a spare, so a gift that went into a box was released at the next PC visit, which made room for the next gift. A Silver run spent hours receiving and releasing Lv 5 starters. A gift the PC would release, or a copy short of the best while the boxes are nearly full, is now settled as skipped, with no gift and no save. Shiny Gen II gifts are always sent. The check rates the gift as the League reward it is. Until it was counted delivered, it rated as a traded copy, the best of its species, so most gifts were still sent. A shiny Red, Blue or Yellow gift is now always sent too. The staged gift's mark goes straight to the rating, so the PC tab never sees it. - Let the loop guard catch Red, Blue and Yellow gift loops. A Gen I gift names the species in capitals and a release does not, so the two never paired. A reload that replays the same maps, trainers and levels no longer counts as progress, so the same save cannot be reloaded forever. The guard reads only new journal entries and is safe to ask from the web page and the game at once. - Stop a waiting Gold, Silver or Crystal run from showing as stalled while the loop guard is tripped. - Stop the overnight watch from marking main-page alerts as seen. Without an alert command, `tools/alert_watch.py` now only prints them, so the timer still hands each one over. - Ask for help when a run loops. The same Pokémon received and released 6 times, or the same save reloaded 6 times, since the last real progress (a badge, catch, evolution, League win, trainer, level or new map) now shows "The run is caught in a loop" on the main page, and the run stops gifts and reloads until it makes progress. - Back off the whole game after a trade times out on the way to the Cable Club, as a full PC already does. A stuck run used to be picked for trade after trade, once per Pokémon it offered. - Repair a Red, Blue or Yellow party nickname that holds a text command byte, when a save loads and when a trade ends. A traded name with a damaged ending drew differently from its bytes, so the party menu never matched and the run could not heal, use Surf or switch. - Write the problems the main page shows to `watchdog-problems.json` in the data directory, so a host can watch them. `tools/alert_watch.sh` runs a command once per new problem. - Stop Red, Blue and Yellow runs from reloading forever when reloads do not help. After 12 reloads in a row without progress, the watchdog gives up the stuck collection project for a while, journals "Gave up on" that goal and lets the next goal run, as Gold, Silver and Crystal already do. A story goal has no project to give up, so the run stops reloading, says so once in the log and shows as stalled. - Give every stored Pokémon a PC value and release the cheapest first, in all six games. A Pokémon's popup on the PC page shows its value, what it is made of and whether it is a spare, kept for a plan or never released. The next five to go carry a mark on their cards. A legendary or one-time event Pokémon, such as a second Mew or Ho-Oh, counts 200, as much as a trained Lv 100 spare, and still goes before any shiny. Ordinary gifts count 80. Keep-locked Pokémon, the best or only copy of a species, the party and eggs are never released automatically. **Release at next PC visit** puts a Pokémon first in the queue, and an only copy asks for its name first. The **Release order** select can favor shiny or DV copies. Visits take the cheapest box by box, so a visit can run longer but costs less per freed slot. The journal names the value of each released Pokémon, and a PC with nothing left to release says so once on the PC page and in the journal. The API is `POST /api/pc/release` and `POST /api/pc/favor`. - Stop Gold, Silver and Crystal runs from walking to a PC forever for room that does not exist. With a full party and every box full of Pokémon a release may not take, such as League rewards from other trainers, a gift sent the run to a PC to deposit a partner. The PC had no room, so the run turned it off and walked back, through every reload, for days. Gifts and the Game Corner prize partner now wait until a slot is free. The watchdog also escalates now: after 12 reloads that did not help, it blocks the stuck collection goal for a while, journals "Gave up on" that goal, and lets the next goal run. - Say why an adventure could not start when none of its autosaves restores. The error now names each reason a save was refused, how many saves it covered and the first one, instead of only how many were tried. A worker that fails before it is ready now sends that reason to the Library, which used to show only "Worker exited before becoming ready" ([#321](https://github.com/afk-sapien/PokeSim/issues/321)). - Let a manager set the next activity from the game page. The **Next activity** picker lists the goals the planner can start now, from its own list. The planner runs the chosen goal once, after upkeep such as healing, and then goes back to its own order. **Cancel** drops it. A goal that cannot run now is refused with the reason. A load, a restart or a stuck run clears the pick, and a stuck run writes "Gave up on" the goal to the journal. The API is `GET /api/goals` and the `pin_goal` and `unpin_goal` controls. - Add a Nicknames switch to each adventure's settings, in all six games. Off, the bot answers NO at every nickname prompt, so caught, gift and reward Pokémon keep their species names. Player and rival naming is unchanged. The switch applies at once, including while the adventure runs, through the new `PATCH /api/v1/adventures/{aid}/play` route. A saved nickname pool that leaves no names now also means no nicknames, where before the Library refused to save it. A Library that never saved a pool still uses the built-in names. - Add a Play style choice to each adventure's settings, in all six games: Balanced, Story first or Collector. Balanced is the default and plays as before. Story first ranks filler side projects below the planned story and Pokédex goals. Collector ranks better DV copy and repeat catch hunts with new Pokédex entries, so they compete on value per frame instead of waiting behind training and races. Story and upkeep goals never move. The choice applies at once, including while the adventure runs, through the `PATCH /api/v1/adventures/{aid}/play` route. An unknown style is refused. - Stop Red, Blue and Yellow DV hunts from catching Pokémon only to release them. A hunt took every species on its map, so a postgame Yellow hunting Tauros caught about 1500 owned Nidoran, Exeggcute and Cubone a day in the Safari Zone and released nearly all of them at the next Pokémon Center. A hunt now reads the wild Pokémon's DVs and catches it only when they beat every copy already held, for the target and for other species alike. Missing and shiny Pokémon are still always caught. - Heal before a boss battle when the party is not ready, in all six games. Before a gym leader, an Elite Four member, the Champion, the Gen I rival in the League or Red, a Pokémon under half its HP, or under half the PP of the attacks it fights with, sends the party to a Pokémon Center first. Each attack's PP counts by how hard it hits, so a spent Flamethrower is not hidden by a full Strength. One approach takes at most three such trips. With no Center in reach, or the trips used, the party goes in as it stands and the journal says "Went in unready". - Keep the image that can restore each host backup, and keep fixes on `main` from sitting outside production. `tools/deploy_host.sh` named each backup after the commit it deployed and kept that image, but the data was written by the image before it, so a backup could outlive the only image that could open it. Each backup now has a json file naming its writer image, and that image is kept. The script also lists the `main` commits a candidate lacks and refuses when one is over a day old, unless `--behind-main` is passed. - Hold every deploy to a live soak. `tools/live_soak.py` plays a candidate and the deployed commit for two game hours each on copies of the live adventures and of post-game saves with a full PC and a League gift backlog, and fails the candidate when it releases, reloads, repeats itself or grows its journal faster than the deployed commit, makes less progress, or keeps catching for a goal without a better copy. `tools/deploy_host.sh` now refuses a commit, a rollback included, without a passing soak report. Red, Blue and Yellow League gifts are now spaced in game time, so a fast run gets the same gift rate per game hour as a slow one. The loop guard keeps its reload counts in the adventure, so a worker restart no longer forgets a save it keeps reloading, and it now pairs gifts and releases of Nidoran♀, Nidoran♂ and Mr. Mime. Stage ladder gate reports record their attempt. - Stop DV hunts from catching a Pokémon and releasing it at the next Pokémon Center, in all six games. A hunt kept a copy whose DVs beat the best one held, but the PC valued copies by level and training, so the new Lv 5 copy was often the first to go. Copies are now judged by one rule in both places. A copy scores as its best final form would fight at Lv 100 with full stat experience, and Attack counts fully when its best physical attack beats its best special one by a quarter, not at all when the special one wins by a quarter, and half otherwise. Attacks are rated by power, accuracy and the same-type bonus, from the family's level-up moves plus the one-time TMs and the HMs the bot teaches that the form can learn, Explosion and Self-Destruct left out. Its rarity is the share of all DV combinations that score as well. A hunt catches a copy only when it is at least twice as rare as the rarest copy held, and still always catches missing and shiny Pokémon. With the boxes nearly full, a catch, a gift or a trade is also skipped when the PC visit it brings would release it or a copy rarer than it, of any species. The Gold, Silver and Crystal post-game hunt for better DVs now picks, catches and ends by this rarity instead of the DV total, and Red, Blue and Yellow value their hunts, protect rare finds from automatic trades and judge a trade's DV gain by it too. The PC releases the most common copies first, and level no longer adds to a copy's value. The party, trained Pokémon, the rarest copy, Keep-locked Pokémon, eggs and plan roles stay. A Red, Blue and Yellow PC visit now plans for every owner release request, as Gold, Silver and Crystal do. The live soak now fails a candidate that releases a new catch at its first PC visit, or releases a copy rarer than every newer copy that pushed it out. When a Red, Blue or Yellow cartridge is set but its TMs cannot be read, the PC now rates and releases nothing and the main page says why, instead of rating with the HMs alone. The live soak report now shows how many Pokémon each adventure releases at its first PC visit, and how many it catches and releases over the whole run. - Let a Red, Blue or Yellow party of field-move holders make room for a trade. A postgame Yellow party held its strongest Pokémon, Pikachu and four Pokémon that were each the only one with Cut, Strength, Surf or Fly, so every trade that needed a boxed Pokémon failed with "No safe unprotected reserve". When no one else may go, the trade now stores the only holder of Cut, then Strength, then Surf, since the game withdraws a partner with that move from the PC when it needs one. The only holder of Fly or Flash still stays. Once the trade is released, the same PC visit deposits the received Pokémon and puts the stored holder back in the party, unless the received Pokémon is locked in the party. - Let a Gold, Silver or Crystal party of field-move holders make room for a trade the same way. When no one else may go, the trade stores the only holder of Whirlpool, then Surf, then Waterfall, the moves the game withdraws a PC partner for. The only holder of Cut, Strength, Fly or Flash stays. - Give each leg of a boss approach its own heal trips, in all six games. Red spent its three trips in Victory Road and went into Lorelei unready in 10 of about 25 League entries over 2 hours, though the Indigo Plateau Center is on the way. A leg now runs from the last Pokémon Center the party stepped into, so a new Center brings three fresh trips, while trips back to the same Center still stop at three. The journal now writes quiet "Healed before" and "PP heal at" rows that stay off the feed. - Keep CI green through a GitHub release-asset outage. The pyboy-rs and pokecore wheels are release assets, and uv checked every one of those links on each sync, so a run failed even with every wheel in its cache. CI now keeps a uv cache per job, keyed on the pyboy-rs and pokecore versions and the lock file, and syncs and exports offline from it first, and goes online with 10 retries only when the cache misses. Later steps skip the sync. The Docker build retries those downloads 10 times. - Let the live soak survive a failed download. When building a commit's environment fails on the network, as on a GitHub 504, it now tries once more offline from uv's cache and says so in its log. The lock still checks every wheel's hash. - List each goal once in the **Next activity** picker, in all six games. A post-game save listed about 1000 goals, most of them "Find Pidgey" once for every route, water and hunt kind. Each goal now names one thing to do, such as "Catch Ditto (missing entry)", "Better Pidgey (DV hunt)", "Train Kabutops to 60" or "Get Articuno", and setting it runs the best ranked way to do it. The picker shows the best 30, and its search box finds the rest by name. - Stop catching Pokémon the PC lets go at its next visit, in all six games. Red caught a higher-level Tentacool with ordinary DVs over a better one it held, and Yellow a Nidorina, because a catch of an owned species still counted a higher level or a new type for the team. Now every catch of a species already owned, on the way, in the Safari Zone, on a hunt or for a returning legendary, is made only when the PC would keep it. Missing Pokémon, shinies and field-move partners are still always caught. The PC now releases only spare stored copies, never a Pokémon deposited from the party at the same visit, so Red no longer released its Lv 43 Weezing while keeping four rarer Lv 5 ones. The live soak now fails a candidate that catches a Pokémon it releases at the next visit, or releases a species' best or only copy, a Keep-locked one or a team member. - Stop a returned legendary from coming back again and again once the PC would not keep it, in all six games. The battle refused the catch, but the return stayed open, so the plan sent the bot back and the retry timer reopened the encounter. A refused return now closes until the next walking milestone, as a caught one does. - Stop Gold, Silver and Crystal from counting a spare as a team member when it joins the party only to give up its held item, and check every Gen II release against the PC values before it starts. A soak of Crystal withdrew five boxed spares, Kabutops, Kingler, Shellder, Goldeen and Seadra, to take their items, then released them. The PC visit counted each one as a team member for the rest of the visit, and the item release skipped the PC values. A spare the item route withdraws only to take its item is now no team member at that visit. The trainee, the League partner and any Pokémon with a role stay team members whatever their box history. Every Gen II release, from the item route, a release visit, a team change, a training partner, a trade or a Link prep, now asks the same release rule before its first press and waits when the rule refuses. The live soak judges a withdrawn spare the same way. Its obtains limit now leaves out catches kept as their species' best copy, so a full box no longer fails a long soak, and the report shows how many there were. The report also gives each catch's rarity and the rarest copy of its species held before it. - Warn when a game goes too long without real progress, in all six games. Every few minutes the watchdog reads each running adventure's progress table and journal. In the story, a badge, a new area, a new Pokédex entry or a League win is progress, and a game is stalled after twice the slowest gap the original six games took at the same badge count, never under 3 hours. In the postgame, a League win, any Pokédex change or a level up is progress, so a trainee in Silver Cave keeps a game going, and a game is stalled after 24 hours. Experience, repeat catches and other journal entries never count, and time stopped, paused, in manual play or in a trade is left out. A stall shows on the main page and sends one ntfy alert, and the alert watch hands it over too. - Judge each live soak catch by its own rarity. A catch now improves the PC when it is a new Pokédex entry or at least twice as rare as the rarest copy of its species held when it arrived, as the catch rule keeps it, and counts as kept while the PC holds it or a rarer copy at the end. Red's second Seel, 1 in 4 against a 1 in 2 new entry, was scored as not kept because the first catch took the credit. - Keep the overnight watch's Gen II quality hunt check working with accounts on. It read the app's pages, so every night since accounts it got a login error and then a traceback. It now reads each adventure's files inside the container, as the alert watch does, so it needs no login in any auth mode. Empty or broken input now fails with a plain line. - Train before another try at a boss that won, in all six games, and warn at once when a game keeps getting stuck in one place. Gold lost to Falkner again and again with a Lv 12 Chikorita and five Pokémon under Lv 6. Once Chikorita fainted, a Metapod that knows only Harden was left, Gold called the battle one that could not end, and the reload went back into it. Now a Pokémon with no damaging move, or an egg, never counts toward a readiness bar or level cap. Falkner waits for a Lv 14 fighter. A loss to a gym leader, the Elite Four, the Champion or Red raises the bar to 2 above the best fighter level that lost, up to 10 above the boss's ace, and the bar is kept across reloads. The Elite Four rooms share one bar. In Gold, Silver and Crystal a battle counts as one that cannot end only when the last Pokémon standing can heal itself, as in Red, Blue and Yellow, so a lost battle plays out to a black out. The watchdog now raises a problem as soon as a game logs 2 stall entries at one place within an hour, in all six games, where it used to wait at least 3 hours. It also raises one when every box is full and nothing has been caught for 24 hours. - `replay_audit.py run --reseed-goals` now keys its goal seeds by `--seed` as well as the save, so two seeds on one save play different rolls. Before, every seed replayed the same game. Seed 7, the default, keeps its old seeds. - Hold the live soak's release counts to the deployed commit on the same copy. A release check, such as a copy rarer than the newer one that pushed it out, now fails only when the candidate does it more often than the deployed commit did in the same soak, or when the deployed run failed. A problem the deployed build already had no longer blocks a deploy. - Send Gold, Silver and Crystal training and hunting walks only to encounter tiles they can reach on a map with wild Pokémon in the cartridge's table. A walk aimed at Victory Road floor outside its walls, found no route and stood in Mr. Pokémon's House while each trainee gained nothing. A training stint that gains no experience, or ends in a reload before it gains any, now marks its map as failed, so the next trainee trains somewhere else. In all six games, a run that blacks out to a gym leader or in the League no longer counts its experience, levels and trainer wins as progress, so the stall alert and the loop guard now fire on a team that keeps losing. - Count a stall reload inside a boss battle as a loss to that boss, in all six games. Gold at Azalea Gym had only two Metapod with Harden left after Bayleef fainted. The battle ran long, the guard reloaded it, and with no loss noted the run went straight back into the same fight. Now any reload during a trainer battle in a boss's room or the League raises that boss's bar, so the team trains first. The trainer, not the room, now names a boss, so rival battles, Giovanni and the Team Rocket executives count too. Crystal at Burned Tower lost its Quilava to the rival and was left with a Togepi that knew only Growl and Charm. A reload there now raises the bar for the story goal under way, and the team trains on the route before the next gym. - Keep the last free PC slot for a better copy, in all six games. Silver filled all 14 boxes with only copies, so no release could free a slot and its quality hunts never started. Now the last slot, counting free party places, takes only a copy the PC rule keeps as a strictly better copy of a species it holds. The release goal runs before anything else whenever slots are wanted, so the replaced copy goes at the next PC visit and the slot is free again. A missing Pokédex entry may take the last slot only when a spare can be released after it. Otherwise the game leaves the encounter and asks for help with Boxes full. - Stop the live soak from flagging a release as pushed out by a newer copy that no release may take. Yellow released a Grimer while holding a rarer gift Mew, the only copy of a new entry. The judge now compares only against newer copies that are not their species' rarest or only copy, not in the party and not set to Keep. - Carry a League kit and heal between rooms from the bag, in all six games. Gold and Crystal reached Lance with the ace's strong attacks spent and no way to heal. At the Indigo Plateau mart the party now buys restores for five rooms and two Revives, the cheapest strong restore first, and keeps half its money for another run. It buys the same kit between tries. Between rooms it revives a fainted fighter, gives a fighter under the boss check's bar the smallest restore that lifts it past, and uses any Ether or Elixir in the bag on its spent strong attacks. A bag that can heal the party counts as a heal for the boss check, so no trip is spent. Over Gold and Crystal on two seeds, each run still reached the Hall of Fame, in 31 League tries in all against 34 without the kit. - Release the lowest-value spare first at a PC visit, of any species and in any box, in all six games. A visit counted scores within 50 points as equal and then took the open box first, to save a box change. Yellow released a Grimer at 1 in 1.4 while a commoner copy at 1 in 1.3 stayed in another box, and a Bellsprout at 1 in 1.6 while a spare Bulbasaur at 1 in 1.4 stayed. Now only an exact score tie chooses by box. A trade's, a partner's or trade demand's points still keep a commoner copy. The live soak now judges a release by the same PC score, so it fails only when a released copy outscores a copy kept. - Take a walked step through a cut tree again only while a partner knows Cut, in Red, Blue and Yellow. Trees grow back, but the walk kept every step it had seen and trusted it later. Yellow on its way to Celadon paced Route 2 for two game hours between two trees it had cut before, after no partner knew Cut. Now it takes the way through Viridian Forest. - Run every command under `tools/load_gate.sh` at nice 10 or lower priority. Agents had to add `nice -n 10` by hand, and a full suite once ran at full priority. A command already at a lower priority keeps it. - Accept a passing live soak for a commit with the same git tree when deploying. A merge commit that changed nothing from its soaked head needed a second 10 minute soak. The tree is read from git, never from the report, so a commit with any change still needs its own soak. - Train before each Johto gym after Falkner and before the League in Gold, Silver and Crystal until the team matches up, as Red, Blue and Yellow do. Gold went into Azalea Gym with a Lv 13 Cyndaquil and a Metapod that knows only Harden, and blacked out to Bugsy's Scyther four times. Each boss's ace now comes from the cartridge's trainer tables, so a randomized run meets its own. The score is the one Red, Blue and Yellow use, and Gen II reads both sides' damage from the battle's own estimate. Before Bugsy and each later Johto gym the team trains on the gym's route while its best score against the ace is under 75 and its best fighter is under 10 levels above the ace. Before the League it trains on Victory Road while a room scores under 60 and the best fighter is under the top ace's level, Lv 50 on a clean cartridge. Without the trainer tables the old level bars stand. In all six games an HM now goes to another member before the ace, so the ace keeps its four attacks. - Count no release in the live soak for a copy missing at one poll and back at a later one. A poll can read the party and the PC while a copy moves between them, and Soak red once failed for a gift Eevee the game never released. - End the turn when a walk finds no route, in all six games, and record a failed attempt on its goal with the map and the target tile. Gold, Silver and Crystal explored for 60 turns with no route and then failed a task, and every third failure reloaded the save. The reload replayed the same missing route after a full stall timeout. Red, Blue and Yellow explored and then ran the stall recovery. Now no reload and no stall event follow. The goal picker passes the target over for a while, and the wait doubles each time the same target fails again. Gold, Silver and Crystal also send a person's errand, a contest search with no grass in reach and a roaming beast's next route only to tiles the map regions join. - Run the overnight watch's private-save tests and Gen II quality hunt check on the local dev instance, and only the health and smoke checks on the hosted app. The quality hunt check failed with HTTP 401 on a hosted app that uses accounts. The watch now reads the dev instance's commit, Python and data folder from its systemd unit, and tests origin/main when it is not running. The main-page alerts stay with the alert timer. - Test that Red, Blue and Yellow do the same thing as Gold, Silver and Crystal in five scripted situations: a gift with a full PC, a stall, a marathon checkpoint, a duplicate catch and a DV hunt. Gifts and hunts already match. The stall ladders differ, and neither generation writes a progress row for a checkpoint or a catch yet. Each gap is marked with the goal system v2 change that fixes it. - Keep a quiet log of progress in all six games: each catch, received Pokémon, release, marathon checkpoint and finished goal, with the game time it happened. Only a checkpoint counts as progress, so catching the same Pokémon again or trading one in counts as nothing. Nothing acts on the log yet, so a stall is still judged the way it was. - Add `tools/replay_progress.py`, which replays a copy of each adventure's journal through today's stall checks and the new progress model side by side, and reports the stalls each one caught, missed and raised in error. The first run on two weeks of play did not meet the bar, so stalls are still judged the old way. - Keep a lasting record of each stall bundle, help request and restart in the adventure's database, for the progress replay. The row for a stall bundle stays after its folder is pruned, and a new game keeps every row. A restart says whether the owner, the supervisor or the app's start caused it. Nothing reads the rows during play. - Tidy the play page. What happens after someone plays by hand is now chosen in the adventure's Settings, and the box under the screen shows only while someone holds control or waits for it. Choosing the next goal is one row across the foot of the plan, and with nothing to choose it shows only the reason. The empty party no longer stretches down the page, and Next says Finish the plan first instead of Still on this one. - Polish the play page row by row. Choose next labels each control Goal, Pokémon and Stop when, and a goal that targets a species offers one search box that lists only the species it can reach. The whole row shares one height and one face. The screen shows one play status, without a Live tag or a Running note in the top bar while the page is connected. The speed select says Speed beside it, and the view link key reads View link. - Add accounts. With `AUTH=accounts` everyone logs in with a password, an OpenID Connect provider or a trusted proxy header. Admins manage the Library and members see only their own adventures and the ones shared with them. The first admin comes from a one-time setup code in the log or from `POKESIM_ADMIN_USER` and `POKESIM_ADMIN_PASSWORD`, and owns every earlier adventure. Admins invite people with one-time links, owners let other people watch or make expiring watch links, trades run only between adventures one person owns, and `pokesim admin reset-password` recovers a locked-out account. With `AUTH` unset a Library stays open until it uses accounts, and an open Library that listens on more than its own machine says once that anyone who reaches it controls PokeSim. `compose.yaml` and `compose.quickstart.yaml` pass `AUTH`, `AUTH_TRUSTED_PROXIES` and the first admin pair from `.env`, and the store templates offer `AUTH`. See [Users and sharing](docs/users-and-sharing.md) and [Authentication](docs/authentication.md). - Add trade offers between people. An owner switches on Open to offers, and people the adventure is shared with send offers from Make a trade, with or without a Pokémon they ask for. Only the owner accepts or declines, a changed Pokémon asks for a second confirm, and unanswered offers expire after 24 hours. Auto trading with other people lets automatic trading pair two owners' adventures when both switch it on. See [Trade offers](docs/trading.md#trade-offers). - Add running limits. An admin sets a server-wide limit on running adventures and an optional per-member limit in Settings. A start over a limit is refused with the numbers, and running adventures are never stopped. Settings suggests 2 per member on an ARM machine with 4 GB of memory or less, otherwise 4. See [Running limits](docs/users-and-sharing.md#running-limits). - Add giving an adventure away and deleting a user. An owner gives an adventure to another person from Sharing. An admin deletes a user after choosing, for each adventure they own, a new owner or Archive, so no adventure is deleted with the account. Either way the old owner's open offers, queued trades and control lease end. See [Delete a user](docs/users-and-sharing.md#delete-a-user). - Keep one Library cartridge shelf that only admins manage. Members get no upload or remove controls and no Game cartridges panel. Everyone who can create adventures sees every Library cartridge and can start an adventure from it. Removing a cartridge is refused while any adventure, of anyone, plays it, and deleting a user leaves every cartridge. The per-user shelves of earlier builds of this branch go on the first start, and every ROM and file stays. See [Cartridges](docs/users-and-sharing.md#cartridges). - Add `pokesim smoke`, a check of a running server through its HTTP API with throwaway users it deletes again, and an API reference at `/docs` for admins. See [API reference and smoke check](docs/authentication.md#api-reference-and-smoke-check). - Fix account and sharing problems from review. Behind a proxy listed in `AUTH_TRUSTED_PROXIES`, logins see the client's own address from `X-Forwarded-For`, so one person's wrong passwords no longer slow everyone. Wrong passwords from strangers slow an account by a minute at most, so nobody can lock someone else out. Sharing refuses an unknown or disabled person, an offer starts at once only for someone who manages both adventures, and losing a share keeps offers sent to it from your own adventure. Archiving a deleted user's adventure stops its game, Resume and Pause keep the control lease when the game never got them, a slow game holds up only its own control lease, and a queued page that closed leaves the line. Stylesheets and health checks skip the account lookups. See [Client addresses behind a proxy](docs/authentication.md#client-addresses-behind-a-proxy). - Fix more account and control problems from a second review. A backup during a control change no longer hangs. Pause and Resume end only the lease they were sent for. A queued page that was hidden too long leaves the line and says so. Forwarded addresses with a port, brackets or an IPv4 address in IPv6 form are read correctly. Only a browser that logged in to an account before skips its slowdown, not any browser at the same address. Losing manage of an adventure ends offers sent from it. Deleting a user checks for the last admin and the new owners in the same step that deletes them, so a refusal leaves nothing half done. A flood of failed logins no longer wipes the counts of other addresses. - Fix more account and control problems from a third review. A forwarded address from the default gateway never counts, so a direct client cannot pick its own address. Nobody can take control while Resume or Pause reaches the game, or while a trade is being set up. Ending control tells the people waiting in line. Changing a share, demoting a user or disabling them never holds up other pages, and ends their offers and their place in line where the new role no longer allows them. Nothing new can belong to a user while they are deleted. The last-admin check counts admins at the moment of the change. A browser counts as known for 30 days at most, and no longer after a password change or the end of that person's sessions. Control stays closed after a backup until every game is back, and background work queued at shutdown still runs. - Fix more account and control problems from a fourth review. A lower role from the identity provider or the proxy now ends offers and places in line the old role allowed, as an admin's change does. Deleting an adventure ends its control and tells the people waiting in line. A role change right after an adventure was deleted no longer fails. A failed cleanup step while deleting a user no longer skips the steps after it. - Fix more account and control problems from a fifth review. An offer that ends while it is being accepted never queues its trade, so no Pokémon moves without both sides agreeing. A page in line whose person can no longer play hears why when control passes on. A press from someone who just lost control never reaches the game after the next person takes over. - Fix more account and control problems from a sixth review. A held button against a slow game no longer ties up the library, presses reach the game in the order they were pressed, and a slow press waits as long as before. A press sent just before control changed hands is dropped by the game. Accepting an offer no longer holds up other offers while both games answer. A page in line whose person can no longer play now says so, instead of saying they were turned down. - Fix more trade and control problems from a seventh review. An offer made while its adventure is given away is refused, so it can never trade the new owner's Pokémon. Accepting an offer whose Pokémon just left in another trade is refused instead of queueing a trade for a Pokémon that is gone. A game that falls behind keeps at most 8 presses waiting, and drops a press that waited over 2 seconds or whose page closed. A Take Control or Resume the game refused no longer turns the player's presses away. Resume and Pause wait behind the presses sent before them, so a late press no longer takes control back without accounts. - Fix more trade and control problems from an eighth review. Someone who cannot press never takes a place in the line. A full line still takes Take Control, Resume and Pause, in order. A Take Control or Resume the game never answered leaves the player's presses working. Resume and Pause without control wait for a Take Control under way. A trade that finished before Accept no longer blocks it. Deleting a user hands each adventure on in one step, as giving it away does. - Fix more control problems from a ninth review. When the game does not answer a Take Control, Resume or Pause, PokeSim asks the game what it did and makes control match. A game that cannot say gets its autopilot back once it answers. Take Control, Resume and Pause no longer fill the press line, and they are dropped after waiting too long. A queued Resume or Pause reads who holds control when its turn comes. - Fix more control problems from a tenth review. PokeSim keeps one wanted state per adventure, so the newest Take Control, Resume or Pause always wins, and a pass of control keeps the owner's choice to resume or pause. A change the game never answered is sent again until it does. Presses from someone who does not hold control are refused before they take a place in the line, and each person has one Take Control in line at a time. A Resume the game made without answering now reports success. A game that hangs no longer slows the other adventures. - Fix more control problems from an eleventh review. When a game refuses Take Control, Resume or Pause, for example during an exchange, control goes back to how it was and the page shows the game's reason. A game that is busy getting ready for an exchange runs the change later instead of losing it. A game that restarted hears its control state again. Losing access and starting a hand-picked trade now tell the game at once, and a backup tells every game at the same time and brings a paused adventure back paused. - Fix more control problems from a twelfth review. A Take Control the game was slow to run no longer runs twice and drops the new player's presses. A refused Resume or Pause keeps the line for control open, and one refused during a backup gives no control back. A stopped adventure starts again on its autopilot instead of pausing itself. A hand-picked trade waits for the game to leave manual control before it prepares. The previous player hears that control was taken only once it was. A change the game keeps refusing is retried less often and logged once. The game page now says when a Pause or Resume will happen once the game can, and shows it as pending until it does. - Fix more trade and control problems from a thirteenth review. Cancelling a hand-picked trade, or shutting down, no longer waits up to 15 minutes for a game that has not left manual control. An adventure stopped during a trade starts again on its autopilot, not paused. A page whose Pause or Resume another person replaced stops saying it is on its way. - Fix two trade problems from a fourteenth review. When stopping one adventure fails after a trade, the trade stays finished and the other adventure still stops. An owner's Start just before the stop after a trade keeps the adventure running. - Fix a control problem from a fifteenth review. When stopping an adventure fails, its next Start still runs on its autopilot and no longer hears an old Pause. - Fix an account problem from a seventeenth review. Deleting the first admin makes the admin who deleted them the Library's owner, so adventures imported from the command line go to a real account. On startup, a Library whose owner was deleted gets its oldest admin as owner, with the adventures that went nowhere. - Fix three more problems from the seventeenth review. The login page goes back only to a page on this site, even when the address it was given has a tab or line break in it. - The first start after upgrading from 0.6.x keeps a copy of the older Library database as `app.sqlite.pre-schema-2-