Problem: `logger.log` positional args were hard to extend, and adding
`sync` support for pre-block notifications required a clean API. Test
stream completion had no user-visible signal. `setup_contest` could
silently overwrite files when a user's `filename` config returned
colliding paths.
Solution: Replace `(msg, level, override)` with `(msg, LogOpts?)` where
`LogOpts` carries `level`, `override`, and `sync`. Sync path calls
`vim.notify` directly; async path uses `vim.schedule` as before. Add
`on_done` callback to `scrape_all_tests`, fired via `on_exit` and
surfaced as a "Loaded N tests." notification. Detect filename collisions
in `proceed()` before touching the filesystem. Migrate all call sites.
Problem: the tolerance field for floating-point comparison was named
`epsilon`, which is an implementation detail, not the user-visible concept.
Solution: rename to `precision` in run.lua type annotations, internal
variables, and comparison logic.
Problem: output comparison used exact string equality after whitespace
normalisation, causing correct solutions to fail on problems where
floating-point answers are accepted within a tolerance (e.g. 1e-6).
Solution: add an optional ui.panel.epsilon config value. When set,
actual and expected output are compared token-by-token: numeric tokens
are compared with math.abs(a - b) <= epsilon, non-numeric tokens fall
back to exact string equality. Per-problem epsilon can also be stored
in the cache and takes precedence over the global default.
Problem: test cases were executed sequentially, each waiting for the
previous process to finish before starting the next. On problems with
many test cases this meant wall-clock run time scaled linearly.
Solution: fan out all test case processes simultaneously. A remaining
counter fires on_done once all callbacks have returned. on_each is
called per completion as before; callers that pass on_each ignore its
arguments so the index semantics change is non-breaking.