Many races in this codebase come from code that reads shared mutable state (a session slot, a cached config, an in-flight “current” pointer, a pending promise resolver), then awaits something, and finally acts on a stale assumption that nothing changed in the meantime. Examples: a teardown writing a session’s terminal status after a replacement already took over the slot; a turn dispatched onto a runtime that a concurrent rebuild has since replaced; a planner serving a set built from an environment snapshot that predates a branch switch; a pending “ask” resolver being claimed by two concurrent answers; a UI action referencing a tray/offer revision that a newer set already superseded.
To avoid this class of bug:
this.sessions.get(id) !== resident), or a “displaced” predicate — and re-check it right before the side effect, not just once at entry.Example pattern used to fix several of these races:
const generation = this.scopeGeneration; // snapshot before awaiting
const result = await doAsyncWork();
if (this.disposed || generation !== this.scopeGeneration) {
// superseded while we were awaiting — do not apply side effects
return;
}
this.apply(result);
Apply this discipline anywhere an async function can be re-entered or raced by another caller touching the same session, cache entry, file, or UI state, and add a regression test that starts the race (hold one operation mid-await, trigger the competing one, then release) to pin the expected outcome.