All work

The Error System

Three concerns, generation, storage, and UI categorisation, kept on one source of truth. Errors are data-driven through ScriptableObject definitions, so adding a new type of flaw means creating a new asset, never changing code. The same definitions that decide what can go wrong on a candidate also build the rejection menu the player navigates to call it out.

Definitions and instances

An ErrorDefinition is the identity and rules for one type of flaw: its category, a path for the picker tree, a scope (element, cross-element, document, profile, or document-set), which element definitions or tags it applies to, a conflict group, an optional helper, a rarity weight, and the hint the rule book shows. At runtime it becomes an ErrorInstance bound to a specific owner, carrying the actual generated data, the bad font name, the typo'd word, the salary figure, and a caught flag set when the player categorises it correctly.

Declarative vs active errors

Most errors are declarative: the definition just sets a flag and the data flows naturally. Salary too high is a flag the generator respects; no cover letter is simply the document skipping an element. Errors that actively corrupt content implement an IErrorHelper interface with an Apply method, a spelling helper, a font-swap helper, a white-text injection helper, each mangling the content in its own way.

The catalogue and generation

ErrorCatalog loads every definition at startup and answers questions: what can fire on this element type, what belongs in this category, resolve these candidates against conflict groups. Each element invokes the generator on itself with a per-day cap, rolls weighted dice, and respects conflict rules; elements never enumerate their own possible errors, the catalogue does. Difficulty knobs per day adjust the cap and weight overrides, and absurd profile-level rules like a Tuesday birthday or wrong horoscope stack independently of field errors.