Devlog

How the 1986 ban is modelled

A dated data row, and the bug behind it

What happened: on Henri Toivonen and Sergio Cresto were killed on the Tour de Corse. Within hours FISA moved to ban Group B from the world championship for 1987 and cancelled the Group S rulebook that was to replace it. Group A became the top class. Everything below is about turning that into software without lying about it.

The wrong way to do it

if (year >= 1987) { hide Group B }

It compiles, it looks right in a screenshot, and it is wrong in three separate ways. It bans the cars everywhere rather than from the championship that banned them. It has no source attached, so nobody can check it in two years. And it deletes entries from a start list, which quietly shrinks the field, as though the ban made 1987's competitors stay home.

They did not stay home. The same semi-works teams turned up in 1987 in Group A cars. That distinction is the whole design.

A ban and an allow-list are not the same rule

This is the distinction the code makes, and it is the one that most games get wrong.

 A class banA period allow-list
Example Group B refused a world-round start from 1987 A historic rally that takes 1970s and 1980s machinery and nothing else
What it means The formula changed under an existing field The start list is the rule: the event exists to be period
What happens to a crew that fails it Keeps its tier, and turns up in legal machinery from the same tier Drops a rung: a works entry that fails a period allow-list is not a works entry, and arrives in club metal
What happens to the field Same size. Substituted, not thinned Same size. Substituted, not thinned

Substitution rather than deletion is the rule everywhere the game refuses an entry, and it applies to more than machinery. A Soviet-era round that will not admit a Western passport does not simply lose those crews and run a short field. It casts inside the rule in the first place, and draws replacements who are allowed to be there.

The bug, because it is the useful part

The rule above was written correctly and shipped. It was also, for months, only checked on one of the two code paths that build a start list. The path it was missing from was the world championship, the one place the rule was actually about.

When it was finally measured on :

Nobody had noticed, because the game looked right: the era pages were correct, the rulebook was correct, the sub-world branch was correct, and the one start list nobody had checked was the headline one. The fix put the gate on both paths, made the substitution step read the ban tables rather than only a historic allow-list, and added a standing test that fails the build if illegal starters reappear.

There is a measurement tool for it in the repository, because "it looks right" is not a test. The same week, an equivalent audit of the passport rule on Soviet-era events found 3,258 crews being cast and then deleted at the start line across 140 affected event-year cells. That went to zero the same way: refuse earlier, and fill the hole.

What happens to your Group B cars

They are not confiscated and they do not vanish from the save. From 1987 they simply cannot be entered in a world round. They can run where they remain legal, they hold and then gain value over the decades, and a team that started in 1973 will eventually be sitting on a collection worth more than its operating budget.

One small display consequence, for the people who will notice: from 1987 the results screen lists Group A first among the classes present, even on an event where a Group B car started. The premier class is a property of the year, not of whoever turned up.

Why this gets its own page

Because the modern sport is the one everybody pictures, and its rules feel like how rallying works. They are not. They are how rallying works now. Two examples this project caught in its own code, both of which had looked entirely reasonable:

The rule that came out of it: before implementing any limit, cost or availability, ask whether it was true in every era from 1973 to 2026. If the answer is no, or unknown, it is era data with a date and a citation, and not a constant in the code.