A one-line class body immediately followed by another statement (e.g.
class Foo: ... on its own line) was reported as a missing branch
("line 1 didn't jump to line 4") with the sys.monitoring core, the
default on Python 3.14. The class body's exit arc gives the class line a
second possible destination, which defeated the resolution of the
sequential fall-through self-arc. Record the true sequential transition
between consecutive lines in a frame so the fall-through isn't
misreported, matching the ctrace core and Python 3.13.
Fixes coveragepy#2167.
Summary
On Python 3.14 (the default sys.monitoring core), a one-line class body immediately followed by another statement is wrongly reported as an uncovered branch, e.g. line 1 didn't jump to line 4. This is class Foo: ... (or any one-line body such as x = 1) on its own line, followed by any statement. Reported in #2167.
Before: 1->4 reported as a missing branch (75%). After: 100%, matching the ctrace core, Python 3.13, EOF, and multi-line class forms.
Fix
The sysmon core records sequential (non-branch) transitions as self-arcs (n, n) and relies on a later pass to resolve them to the real destination, but only when the line has a single possible destination. A one-line class body's exit arc (1, -1) gives the class line a second possible destination, so the fall-through self-arc (1, 1) was never resolved to (1, 4), and 1->4 was reported missing.
The fix records the true sequential transition between consecutive lines in a frame (like the ctrace and pytracer cores already do), in sysmon_line_arcs. last_line is tracked per CodeInfo and cleared on PY_RETURN so recursive frames of the same code object aren't joined by a bogus arc. The change is localized to coverage/sysmon.py and touches neither the parser's arc analysis nor the C tracer.
Tests
Added SimpleArcTest.test_bug_2167 in tests/test_arcs.py using the existing arc-testing helpers. It covers the exact 3-line repro, a non-... one-line body, and a following branch statement. The test fails on main (Wrong missing branches: [] != [(1, 4)]) and passes with the fix.
Verified locally on CPython 3.14.6:
Disclosure: prepared with AI assistance; reviewed and verified locally.