kanban: verify openBoard() cannot race a stalled attach, and close morph#305 as invalid - #333
Merged
Merged
Conversation
…rd() morph#305 claimed BoardBridge::openBoard() dispatched right after construction races the handler's registration and can fail fast with "handler not bound", inferred from reading rather than reproduced. It cannot: OpenBoard is payload-keyed (BRIDGE_MODEL_KEY, board_model.hpp), so BridgeHandler::execute() routes it through Bridge::attachHandlerAsync (bridge.hpp, landed in #42, predating this issue), whose own contract is that a keyed action's attach step never throws "handler not bound" out of execute() -- it waits for the attach round trip and dispatches from inside that completion, regardless of mode. Verified rather than assumed: a new test drives a real Socket-mode client through FaultProxy with the attach reply itself delayed 300ms, then calls openBoard() the instant the presenter is constructed -- the most adversarial ordering the issue describes. It reports the board correctly once the delayed reply lands; no failed() ever fires. No production change: the guarantee already held. This closes morph#305 with the counter-evidence. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
morph#305 asked to gate
Main.qml'sopenBoard()dispatch onBoardBridge::bound(restoring the relay #313 removed), on the premise that a click-triggeredopenBoard()can race the handler's registration and fail fast with"handler not bound"before its attach settles — especially in Remote/Socket mode.Re-triaged rather than implemented as filed: the premise is false against current
master, verified with a real reproduction rather than by re-reading the same source the original triage did.What the code actually guarantees
OpenBoardis payload-keyed (BRIDGE_MODEL_KEY(BoardModel, OpenBoard, &OpenBoard::projectId),board_model.hpp:436).BridgeHandler::execute()(morph/core/bridge.hpp:1967-2007) routes a payload-keyed action throughBridge::attachHandlerAsync, whose own doc comment onexecute()(:1948-1952) is explicit: "A payload- or result-keyed action's attach/promote step never throws out of this call, even when the backend refuses it ... the failure is instead delivered through the returned Completion's.onError(...)." The dispatch itself runs from inside the attach's completion callback (:1996-2006) — there is no window whereopenBoard()can observe"handler not bound", in any mode, because it never takes the fail-fastexecuteViapath that produces that error at all. That path (:2043) is reserved for non-keyed actions on an already-attached handler.attachHandlerAsyncpredates the issue by a wide margin (#42, merged well beforemaster@9371c1a0) — this is not a fix that landed after the issue was filed, the guarantee already held when it was triaged.The original triage (comment on #305) was read-only by its own admission ("I did not stand up a fresh Socket-mode repro myself") and reasoned from
executeVia's fail-fast contract without noticingexecute()'s keyed-action branch dispatches through a different path entirely.Verification
New test in
test_board_presenter.cppdrives a realMode::Socketclient throughFaultProxy, with the attach reply itself delayed 300ms viadelayReply()— the most adversarial timing this rung's transport can produce — then callsopenBoard()the instant the presenter is constructed, exactly the ordering #305 describes. It reports the board correctly once the delayed reply lands;failed()never fires.Change
No production code changes — the guarantee already held. Adds one regression test locking it in, so a future change to
execute()'s keyed-action path that reintroduced the race would be caught here.Closes #305 (as invalid — see the issue comment with full evidence).
🤖 Generated with Claude Code