Category
Technical Debt (cleanup, refactor)
Component
Host Runtime
Description
MailboxState is a single 13-value enum that carries four unrelated kinds of
thing in one shared-memory word. This is the unfinished half of the
async-preparation pipeline's E2 step: "let the mailbox express only phase, and
store the preparation disposition separately." The disposition half shipped in #1650
(48dc78bf, as a side effect of unrelated work rather than any E2 PR):
MailboxPreparationDisposition (worker_manager.h:84-88) got its own frame
offset, its own validation, and a poison path for out-of-enum values. The
phase half did not.
Today's members, grouped by what they actually are (hit counts across src/ +
python/):
| Kind |
Members |
Uses |
| Task phase — the only kind E2 wants to keep here |
IDLE=0 · TASK_READY=1 · FRAME_STAGED=8 · TASK_LAUNCHED=9 · TASK_DONE=2 · TASK_FAILED=10 |
— |
| Init lifecycle |
INIT_READY=6 · INIT_FAILED=7 |
30 · 17 |
| Control channel |
CONTROL_REQUEST=4 · CONTROL_DONE=5 |
12 · 17 |
| Directives written by the peer |
SHUTDOWN=3 · ACTIVATE=11 · PREPARE_READY=12 |
32 · 10 · 8 |
ACTIVATE is not an observed state at all — it is CAS-written into the word
as a command (worker_manager.cpp:724 does FRAME_STAGED -> ACTIVATE).
Three consequences, none of which a rename fixes:
1. The phase values are interleaved, so "is this a phase?" needs a hand-written
set. They occupy 1, 2, 8, 9, 10, with non-phase values at 3–7, 11, 12. The
resulting predicate is spelled out by hand, e.g. worker.py:2702:
live_states = (_TASK_READY, _PREPARE_READY, _ACTIVATE, _FRAME_STAGED, _TASK_LAUNCHED)
Two directives mixed with three phases in one tuple — exactly the conflation E2
exists to remove. Every such set must be maintained by hand and can silently
disagree with the next one.
2. The TASK_ / FRAME_ prefixes are load-bearing, not noise. The step's
target vocabulary is READY / STAGED / LAUNCHED / DONE / FAILED; the code needs
the prefixes only to disambiguate from INIT_READY and CONTROL_DONE in the
same enum. So renaming before splitting produces a strictly worse state —
READY sitting next to INIT_READY. The rename is a consequence of the
split, not a substitute for it.
3. FRAME_STAGED already means two different things in one header.
MailboxState::FRAME_STAGED = 8 (:77) and
WorkerProgressKind::FRAME_STAGED = 0 (:291). Same identifier, same file,
different values.
The constraint that shapes any fix: this word is a cross-process wire
contract, not an internal enum. It is a volatile int32_t at
MAILBOX_OFF_STATE mutated by mailbox_compare_exchange_state, in shared
memory between a parent and its forked children — the POD boundary in
.claude/rules/codestyle.md §8. And the Python side re-declares all 13
values by hand as module constants (worker.py:310-329), with no generation
or assertion tying them to the C++ enum.
Therefore: renaming enumerators is free (compile-time only); renumbering is
not — an already-running child and a freshly started parent must agree on the
integers. Any fix has to keep the numeric values fixed or ship a synchronized
change on both sides.
Not on the critical path for anything today. Filing it so the scope is on record
rather than being re-estimated as "just a rename" — which is how it was
previously (mis)scoped.
Location
Verified against upstream/main@4739cbf4:
- `src/common/hierarchical/worker_manager.h:60-82` — `MailboxState`, the 13-value enum
- `src/common/hierarchical/worker_manager.h:77` — `MailboxState::FRAME_STAGED = 8`
- `src/common/hierarchical/worker_manager.h:290-294` — `WorkerProgressKind`, whose `FRAME_STAGED = 0` (`:291`) collides by name
- `src/common/hierarchical/worker_manager.h:117` — `MAILBOX_OFF_STATE`, the shared-memory offset of the word
- `src/common/hierarchical/worker_manager.cpp:724` — `ACTIVATE` CAS-written as a directive (`FRAME_STAGED -> ACTIVATE`)
- `python/simpler/worker.py:310-329` — all 13 values re-declared by hand, no single source of truth
- `python/simpler/worker.py:2702` — `live_states`, a hand-written set mixing directives with phases
Proposed Fix
Split by concern while keeping every numeric value unchanged, so nothing on
the wire moves:
MailboxTaskPhase — IDLE, TASK_READY, FRAME_STAGED, TASK_LAUNCHED,
TASK_DONE, TASK_FAILED
MailboxInitState — INIT_READY, INIT_FAILED
MailboxControlState — CONTROL_REQUEST, CONTROL_DONE
MailboxDirective — SHUTDOWN, ACTIVATE, PREPARE_READY
They continue to share the one word, with value ranges that do not overlap, so
the split is source-level only and no running pair can disagree.
Then, and only then, drop the now-redundant TASK_ / FRAME_ prefixes, and
rename WorkerProgressKind::FRAME_STAGED or the phase member so the collision
in worker_manager.h goes away.
Independently worth doing and cheap: give the Python constants a generation step
or a start-up assertion against the C++ values, so the two cannot drift
silently. That is the part with real (if latent) risk today — a wrong constant
there is a silent cross-process protocol mismatch, not a compile error.
Priority
Low (no impact today, good to fix eventually)
Related: #1650 (delivered the disposition half) · #1728 and #1739 (E1/E1', the
sibling step, both complete)
Category
Technical Debt (cleanup, refactor)
Component
Host Runtime
Description
MailboxStateis a single 13-value enum that carries four unrelated kinds ofthing in one shared-memory word. This is the unfinished half of the
async-preparation pipeline's E2 step: "let the mailbox express only phase, and
store the preparation disposition separately." The disposition half shipped in #1650
(
48dc78bf, as a side effect of unrelated work rather than any E2 PR):MailboxPreparationDisposition(worker_manager.h:84-88) got its own frameoffset, its own validation, and a poison path for out-of-enum values. The
phase half did not.
Today's members, grouped by what they actually are (hit counts across
src/+python/):IDLE=0·TASK_READY=1·FRAME_STAGED=8·TASK_LAUNCHED=9·TASK_DONE=2·TASK_FAILED=10INIT_READY=6·INIT_FAILED=7CONTROL_REQUEST=4·CONTROL_DONE=5SHUTDOWN=3·ACTIVATE=11·PREPARE_READY=12ACTIVATEis not an observed state at all — it is CAS-written into the wordas a command (
worker_manager.cpp:724doesFRAME_STAGED -> ACTIVATE).Three consequences, none of which a rename fixes:
1. The phase values are interleaved, so "is this a phase?" needs a hand-written
set. They occupy 1, 2, 8, 9, 10, with non-phase values at 3–7, 11, 12. The
resulting predicate is spelled out by hand, e.g.
worker.py:2702:Two directives mixed with three phases in one tuple — exactly the conflation E2
exists to remove. Every such set must be maintained by hand and can silently
disagree with the next one.
2. The
TASK_/FRAME_prefixes are load-bearing, not noise. The step'starget vocabulary is
READY / STAGED / LAUNCHED / DONE / FAILED; the code needsthe prefixes only to disambiguate from
INIT_READYandCONTROL_DONEin thesame enum. So renaming before splitting produces a strictly worse state —
READYsitting next toINIT_READY. The rename is a consequence of thesplit, not a substitute for it.
3.
FRAME_STAGEDalready means two different things in one header.MailboxState::FRAME_STAGED = 8(:77) andWorkerProgressKind::FRAME_STAGED = 0(:291). Same identifier, same file,different values.
The constraint that shapes any fix: this word is a cross-process wire
contract, not an internal enum. It is a
volatile int32_tatMAILBOX_OFF_STATEmutated bymailbox_compare_exchange_state, in sharedmemory between a parent and its forked children — the POD boundary in
.claude/rules/codestyle.md§8. And the Python side re-declares all 13values by hand as module constants (
worker.py:310-329), with no generationor assertion tying them to the C++ enum.
Therefore: renaming enumerators is free (compile-time only); renumbering is
not — an already-running child and a freshly started parent must agree on the
integers. Any fix has to keep the numeric values fixed or ship a synchronized
change on both sides.
Not on the critical path for anything today. Filing it so the scope is on record
rather than being re-estimated as "just a rename" — which is how it was
previously (mis)scoped.
Location
Verified against
upstream/main@4739cbf4:Proposed Fix
Split by concern while keeping every numeric value unchanged, so nothing on
the wire moves:
MailboxTaskPhase—IDLE,TASK_READY,FRAME_STAGED,TASK_LAUNCHED,TASK_DONE,TASK_FAILEDMailboxInitState—INIT_READY,INIT_FAILEDMailboxControlState—CONTROL_REQUEST,CONTROL_DONEMailboxDirective—SHUTDOWN,ACTIVATE,PREPARE_READYThey continue to share the one word, with value ranges that do not overlap, so
the split is source-level only and no running pair can disagree.
Then, and only then, drop the now-redundant
TASK_/FRAME_prefixes, andrename
WorkerProgressKind::FRAME_STAGEDor the phase member so the collisionin
worker_manager.hgoes away.Independently worth doing and cheap: give the Python constants a generation step
or a start-up assertion against the C++ values, so the two cannot drift
silently. That is the part with real (if latent) risk today — a wrong constant
there is a silent cross-process protocol mismatch, not a compile error.
Priority
Low (no impact today, good to fix eventually)
Related: #1650 (delivered the disposition half) · #1728 and #1739 (E1/E1', the
sibling step, both complete)