Conversation
Yaraslaut
force-pushed
the
feature/827-multimodel-bridge-core
branch
4 times, most recently
from
September 26, 2026 19:26
e6e591b to
d6eed40
Compare
…elBridgeCore Two example rungs (bookmarks' BookmarkFormsController, kanban's ProjectAdminPresenter) independently hand-write an actionType -> handler routing table because GenericModelBridgeCore<Model, Sharing> and its forms facade FormsControllerCore are single-Model templates by construction and cannot express dispatch across several registered models. Add morph::qt::bridge::MultiModelBridgeCore<Sharing, Model...>: it composes one GenericModelBridgeCore<Model, Sharing> per Model in the pack and routes a submitted actionType to whichever one serves it. The routing is derived, not declared: GenericModelBridgeCore::servesAction (added alongside its own BridgeHandler::servesAction, backed by a new ActionExecuteRegistry::contains<Sharing> existence check) answers "does this Model recognise this action" directly from the same ActionExecuteRegistry BRIDGE_REGISTER_ACTION already populates, so nothing here re-states a mapping that already exists as data. Models are tried in pack order via a std::apply fold over a tuple of GenericModelBridgeCore instances, so the one Model that matches dispatches through its own already-correct execute() rather than a second, hand-duplicated copy of its completion-wiring. An action registered on more than one Model is a configuration bug, asserted in debug builds rather than resolved silently. An unrouted action resolves onError directly with "no model in this client serves action '<id>'" -- the exact wording both hand-written routers already agreed on independently. morph::qt::forms::MultiModelFormsControllerCore<Sharing, Model...> is the forms-facing facade over it, mirroring FormsControllerCore's schemasJson()/submitIfValid()/fetchOptions() surface. Extract the owning-constructor's private pool/executor/backend bundle, previously duplicated verbatim in GenericModelBridgeCore, into a shared morph::qt::bridge::detail::OwnedLocalBridge used by both cores -- a pure refactor with no behavior change, so the framework does not reintroduce the same "written twice" shape this change exists to remove from examples/. Kanban's ProjectAdminPresenter is deliberately not retrofitted here (see the following commit) -- its routing has per-action side effects (re-decoding a body for a typed signal, installing a session, redacting a token) that a generic routing table cannot serve without also templating those effects. Towards morph#827 (the retrofit that proves this core lands next). Claude-Session: https://claude.ai/code/session_018fEUahMFF32wQLiWjbsfkc Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Yaraslau Tamashevich <yaraslau.tamashevich@gmail.com>
…ollerCore BookmarkFormsController hand-rolled exactly what morph::qt::forms:: MultiModelFormsControllerCore (previous commit) now provides generically: one BridgeHandler per model (AuthModel, BookmarkModel, TagModel) and an actionType -> handler routing table, plus a try/catch converting the routing throw into the same onError shape every other failure takes. Delete BookmarkFormsController entirely. FormsBridge now composes MultiModelFormsControllerCore<NoSharing, AuthModel, BookmarkModel, TagModel> directly, over the Bridge&/IExecutor* AppContext::onReady() hands it -- same pattern examples/pastebin's FormsBridge already uses for FormsControllerCore<PasteModel> (#828). The rung's existing suite (test_bookmark_qml_bridges.cpp) already covered all six actions' routing and the unrouted-action error message end-to-end through FormsBridge, so it proves the auto-derived routing reproduces the hand-written table's behaviour unchanged -- verified: same 837 assertions across 130 test cases pass unmodified, including the six-action routing case and the CreateBookmarks/ListSharedFeed unrouted-action case. Update every other place that named BookmarkFormsController by symbol (bookmark_schemas.hpp, main_wasm.cpp, polls' and kanban's own doc comments comparing their shape to it, the DynamicForm choiceless-controller test) to point at FormsBridge/MultiModelFormsControllerCore instead, so no comment is left describing a class that no longer exists. Kanban's ProjectAdminPresenter comment is updated to explain, rather than merely note, why its own routing stays hand-written: its per-action side effects (re-decoding CreateProject's body for a typed signal, installing a session on Login, redacting a token) are tangled into the routing in a way MultiModelBridgeCore's generic dispatch cannot serve without also templating those effects -- confirmed by reading ProjectAdminPresenter::submitForm and BookmarkFormsController:: dispatch side by side, per the triage's own named contingency for this case. Closes #827 Claude-Session: https://claude.ai/code/session_018fEUahMFF32wQLiWjbsfkc Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Yaraslau Tamashevich <yaraslau.tamashevich@gmail.com>
… doc FormsBridge's "Why this class holds a CallbackScope" comment claimed every submitIfValid reply resolves through the executor, never inline -- true for a routed action's Completion, but not for an unrouted one: dispatch through morph::qt::forms::MultiModelFormsControllerCore reports "no model in this client serves action" via onError synchronously, on submitIfValid's own call frame, since no model's Completion was ever created. Pre-existing behaviour (the deleted BookmarkFormsController's try/catch around a throwing router had the same synchronous-onError shape for this one case) that the doc comment never carved out; noted while touching this file for #827's MultiModelFormsControllerCore retrofit. Claude-Session: https://claude.ai/code/session_018fEUahMFF32wQLiWjbsfkc Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Yaraslau Tamashevich <yaraslau.tamashevich@gmail.com>
Yaraslaut
force-pushed
the
feature/827-multimodel-bridge-core
branch
from
September 26, 2026 20:13
d6eed40 to
f45a4d4
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
This branch has not been deployed
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::qt::bridge::MultiModelBridgeCore<Sharing, Model...>, a variadic sibling ofGenericModelBridgeCore<Model, Sharing>for a rung whose forms span more than one registered model. It composes oneGenericModelBridgeCore<Model, Sharing>perModelin the pack and routes a submitted action-type id to whichever one serves it via a new existence-check (BridgeHandler::servesAction/GenericModelBridgeCore::servesAction, backed byActionExecuteRegistry::contains<Sharing>) — the routing is derived from the sameActionExecuteRegistryBRIDGE_REGISTER_ACTIONalready populates, not a hand-writtenactionType -> Modeltable.morph::qt::forms::MultiModelFormsControllerCore<Sharing, Model...>, the forms-facing facade mirroringFormsControllerCore'sschemasJson()/submitIfValid()/fetchOptions()surface.examples/bookmarks'FormsBridgeto compose the new core directly (AuthModel,BookmarkModel,TagModel), deleting the hand-rolledBookmarkFormsControllerentirely — the example proof this seam works end-to-end.GenericModelBridgeCore) into a sharedmorph::qt::bridge::detail::OwnedLocalBridge.Kanban's
ProjectAdminPresenteris deliberately not retrofitted: its routing has per-action side effects (re-decoding a body for a typed signal, installing a session, redacting a token) that a generic routing table can't serve without also templating those effects — confirmed by reading it side-by-side with the deletedBookmarkFormsController::dispatch, per the issue's own named contingency for this case.Closes #827
Verification
-Weverything -Werror), including bookmarks' 130-case suite (all six actions' routing + the unrouted-action error path) and a new dedicatedtest_multi_model_bridge_core.cppcovering the core directly.MORPH_BUILD_DOCUMENTATION=ON,WARN_AS_ERROR = FAIL_ON_WARNINGS) passes clean./simplify(4-angle pass, twice — per-phase and whole-branch) and scoped/code-review(medium per commit, high-effort whole-branch) agents; all real findings addressed (a move-vs-copy inefficiency was fixed by composingGenericModelBridgeCoreinstead of duplicating its completion-wiring; a missingnoteRegistryReadcall in the newcontains<Sharing>; two doc-accuracy issues indocs/spec/forms/forms.md; one pre-existing doc-precision gap inFormsBridge's own comment, fixed in its own commit).Test plan
ctestfull suite green (2005/2005)examples/bookmarkssuite green (130/130), including routing + unrouted-action casesmulti_model_bridge_core/forms_controller_coreunit tests green🤖 Generated with Claude Code
https://claude.ai/code/session_018fEUahMFF32wQLiWjbsfkc