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: <c-n>/<c-p> in the I/O view buffers required the cursor
to leave the source file to work, re-ran the solution on each
press, and gave no indication of which test was active. The
workflow is better served by :CP run <n> for a specific test or
:CP panel for full inspection.
Solution: remove navigate_test, next_test_key/prev_test_key config
options, and the associated current_test_index state field.
Problem: toggle_interactive() had its condition inverted — it blocked
:CP interact on non-interactive problems while showing the message "This
problem is interactive", and passed through on interactive ones. The
panel guard in toggle_panel() was also missing a nil-check on
contest_data.index_map, which could crash if the index map was absent.
Solution: invert the toggle_interactive() guard to match the symmetrical
pattern in toggle_view(), fix the error message to say "not interactive",
and add the missing index_map guard. Also handle the stress panel type
in M.disable() so :CP stress can be toggled off.
Problem: the hooks API conflated distinct lifecycle scopes under a flat
table with inconsistent naming (setup_code, before_run, setup_io_input),
making it hard to reason about when each hook fires.
Solution: introduce two namespaces — hooks.setup.{contest,code,io} for
one-time initialization and hooks.on.{enter,run,debug} for recurring
events. hooks.setup.contest fires once when a contest dir is newly
created; hooks.on.enter is registered as a buffer-scoped BufEnter
autocmd and fires immediately after setup.code. The provisional buffer
setup_code callsite is removed as it ran on an unresolved temp buffer.