Skip to content

Add: check the mailbox wire constants against the C++ enums at import - #1765

Merged
ChaoWao merged 1 commit into
mainfrom
mailbox-state-drift-guard
Aug 11, 2026
Merged

ChaoWao merged 1 commit into
mainfrom
mailbox-state-drift-guard

Conversation

@ChaoWao

@ChaoWao ChaoWao commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

The mailbox state word and preparation-disposition word are a cross-process contract — a parent writes them, its forked child reads them through shared memory. Both sides declared the values independently, with nothing tying the two declarations together:

  • C++ — MailboxState (13 values) and MailboxPreparationDisposition in worker_manager.h
  • Python — the same values re-declared by hand in simpler/worker.py

That combination has no failure signal. The declarations are in different languages, so a divergence is not a compile error. The resulting value is a legal state, just a different one, so it is not corruption either. A parent storing 9 for TASK_LAUNCHED against a child reading 19 produces a child waiting on a state its parent never publishes — a hang or misrouted frame, far from the edit that caused it.

This is the drift-guard half of #1764. The MailboxState enum split stays open there.

Approach

The binding now exports both enums as name→value tables, right next to the four mailbox constants worker.py already imports from it for exactly this reason (worker_bind.h:838-841). The pattern isn't new — it just didn't cover the enums.

simpler.worker keeps its literals — they're read on the mailbox polling path and an attribute lookup per poll isn't worth paying — and checks them against the native tables once at import. It reports every disagreement in one message rather than the first, since a renumbering usually moves several values at once, and separately rejects an enumerator the native side has that Python doesn't: that's a state a child can legitimately publish and this module wouldn't recognise.

Nothing is renumbered. The change is additive on the C++ side, and a check plus one new constant on the Python side.

What writing the check immediately found

MailboxPreparationDisposition::NONE exists in C++ but was never declared in Python — the parent writes it when claiming a frame, and no child ever publishes it. Python is right to reject NONE as a published disposition (worker.py:2848), so I declared the constant with that distinction stated rather than loosening the check to ignore it.

Testing

Check Result
Python UT 1311 passed / 13 skipped (up 4 from the new tests)
C++ UT (ctest -LE requires_hardware) 92/92
pyright, clang-format clean

The guard was verified against a real divergence, not a simulated one. I renumbered TASK_LAUNCHED from 9 to 19 in worker_manager.h, rebuilt, and confirmed import simpler.worker fails with both values named:

RuntimeError: MailboxState constants in simpler.worker disagree with the C++ enum:
TASK_LAUNCHED: python=9 c++=19. These cross a process boundary, so the mismatch
would surface as a hung or misrouted child rather than an error.

The enum was then restored and rebuilt. Tests pin both directions — that every declared value matches, and that the guard rejects a divergence rather than passing it through — with the perturbation cases restoring what they touch so the rest of the suite is unaffected.

Onboard sweeps were not run: no runtime behavior changes, and the added code executes once at import.

The mailbox state word and the preparation-disposition word are a cross-process
contract: a parent writes them and its forked child reads them through shared
memory. Both sides declared the values independently -- MailboxState and
MailboxPreparationDisposition in worker_manager.h, and thirteen plus two hand
written module constants in simpler/worker.py -- with nothing tying the two
declarations together.

That combination has no failure signal. The declarations are in different
languages, so a divergence is not a compile error; the resulting value is a
legal state, just a different one, so it is not corruption either. A parent
storing 9 for TASK_LAUNCHED against a child reading 19 produces a child that
waits on a state its parent never publishes. It surfaces as a hang or a
misrouted frame, at a distance from the edit that caused it.

The binding now exports both enums as name-to-value tables, next to the four
mailbox constants worker.py already imports from it for exactly this reason.
simpler.worker keeps its own literals -- they are read on the mailbox polling
path, and an attribute lookup per poll is not worth paying -- and checks them
against the native tables once at import. The check reports every disagreement
in one message rather than the first, since a renumbering usually moves several
values at once, and separately rejects an enumerator the native side has and
Python does not: that is a state a child can legitimately publish and this
module would not recognise.

Writing the check surfaced one such gap immediately. MailboxPreparationDisposition
has a NONE member that Python never declared, because the parent writes it when
it claims a frame and no child ever publishes it. Python is right to reject NONE
as a *published* disposition, so the constant is now declared with that
distinction stated rather than the check being loosened to ignore it.

Renumbering is what this cannot allow, and nothing here renumbers anything: the
change is additive on the C++ side and a check plus one new constant on the
Python side.

Tests pin both directions -- that every declared value matches the native table,
and that the guard actually rejects a divergence rather than passing it through.
The rejection cases restore the value they perturb so the rest of the suite is
unaffected.

Verification: the guard was confirmed against a real divergence, not a simulated
one -- renumbering TASK_LAUNCHED from 9 to 19 in worker_manager.h and rebuilding
made `import simpler.worker` fail with both values named; the enum was then
restored and rebuilt. 1311 Python unit tests, 92/92 C++ unit tests
(ctest -LE requires_hardware), pyright and clang-format clean.

Refs #1764. This is the drift-guard half of that issue; the MailboxState enum
split remains open there.
@coderabbitai

coderabbitai Bot commented Aug 10, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 41fde6bc-64e4-4256-8228-d37d2dc5c34e

📥 Commits

Reviewing files that changed from the base of the PR and between 8616c73 and 365fb9f.

📒 Files selected for processing (4)
  • python/bindings/worker_bind.h
  • python/simpler/task_interface.py
  • python/simpler/worker.py
  • tests/ut/py/test_worker/test_mailbox_atomics.py

📝 Walkthrough

Walkthrough

The native bindings now export mailbox enum values. Python re-exports these tables and validates its mailbox constants during worker import. Unit tests cover matching values, renumbered constants, and undeclared enumerators.

Changes

Mailbox wire validation

Layer / File(s) Summary
Native wire-value exports
python/bindings/worker_bind.h, python/simpler/task_interface.py
The native bindings export mailbox state and preparation-disposition values. task_interface imports and publicly exports both tables.
Import-time validation and tests
python/simpler/worker.py, tests/ut/py/test_worker/test_mailbox_atomics.py
The worker validates Python mailbox constants against native values during import. Tests cover matching values, renumbered constants, and undeclared enumerators.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Poem

I’m a rabbit guarding wires in the night,
Native numbers must match just right.
States and dispositions hop in line,
Bad values thump an error sign.
Tests restore each burrowed byte—
Then all the mailboxes work bright.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the import-time consistency check between mailbox wire constants and C++ enums.
Description check ✅ Passed The description directly explains the mailbox enum consistency check, the missing constant, implementation approach, and test results.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant