Surfaced while implementing #568 (PR #585), filed per that PR's "Gaps in #567's surface" section rather than folded in, since it needs a decision and change that belongs with #571, not with #568.
The finding
Completion delivers to whatever executor the caller passes it. For registration, that caller is Bridge, which owns no event loop and no strand of its own — so the only executor Bridge's four dispatch sites (registerHandlerImpl, ensureBoundAsync, attachHandlerAsync, assignHandlerPrimary) can honestly name is exec::detail::inlineExecutor(): "deliver wherever you settled." That is exactly the prose contract #567/#568/#569 exist to replace with something structural — but naming inlineExecutor() doesn't structurally guarantee anything; it just restates "wherever you settled" as a value instead of as a sentence in a doc comment.
bindModel/promoteModel (#567's structural surface) made backends honest about which thread they settle on. They did not give Bridge, the framework's own caller of those verbs, a thread to name. So:
What's missing
Bridge would need to hold an executor bound to the thread that runs ~Bridge, and no ticket in the #522 set (#568/#569/#570/#571) currently owns adding one. This is the difference between "the guarantee is structural" (true of backends now) and "the decision is structural" (not yet true of Bridge) — see PR #585's "Decisions still open" section for the fuller writeup.
Status
Verified by reading: Bridge's four dispatch sites in include/morph/core/bridge.hpp all pass inlineExecutor(); exec::detail::inlineExecutor()'s definition confirms it runs the continuation inline on whichever thread settles the Completion. Not a reproduced defect — a scope gap in the #522 ticket set. Relevant to #571 before it claims its acceptance criteria are met.
🤖 Generated with Claude Code
Surfaced while implementing #568 (PR #585), filed per that PR's "Gaps in #567's surface" section rather than folded in, since it needs a decision and change that belongs with #571, not with #568.
The finding
Completiondelivers to whatever executor the caller passes it. For registration, that caller isBridge, which owns no event loop and no strand of its own — so the only executorBridge's four dispatch sites (registerHandlerImpl,ensureBoundAsync,attachHandlerAsync,assignHandlerPrimary) can honestly name isexec::detail::inlineExecutor(): "deliver wherever you settled." That is exactly the prose contract #567/#568/#569 exist to replace with something structural — but naminginlineExecutor()doesn't structurally guarantee anything; it just restates "wherever you settled" as a value instead of as a sentence in a doc comment.bindModel/promoteModel(#567's structural surface) made backends honest about which thread they settle on. They did not giveBridge, the framework's own caller of those verbs, a thread to name. So:Bridge— which is all of them from application code's perspective.What's missing
Bridgewould need to hold an executor bound to the thread that runs~Bridge, and no ticket in the #522 set (#568/#569/#570/#571) currently owns adding one. This is the difference between "the guarantee is structural" (true of backends now) and "the decision is structural" (not yet true ofBridge) — see PR #585's "Decisions still open" section for the fuller writeup.Status
Verified by reading:
Bridge's four dispatch sites ininclude/morph/core/bridge.hppall passinlineExecutor();exec::detail::inlineExecutor()'s definition confirms it runs the continuation inline on whichever thread settles theCompletion. Not a reproduced defect — a scope gap in the #522 ticket set. Relevant to #571 before it claims its acceptance criteria are met.🤖 Generated with Claude Code