Problem
`IModelHolder`'s `attachActionLog`/`recordIfAttached`/`hasActionLog` operate
entirely on the holder wrapper — never on the wrapped model instance. This means
a model that itself wants to read back its own journal (e.g. to derive an
in-model feature like an activity stream from `IActionLog::entries(entityKey)`)
has no way to get at the same `IActionLog` the holder's own auto-append writes
entries into: `ModelFactory::create()` auto-attaches the process-wide
journal to the holder on every registry-constructed instance, and never calls
any model-level method.
Concretely: a registry-constructed model (the kind `RemoteServer`/`App` actually
dispatches through in production) has no mechanism to keep its own
`std::shared_ptr` in sync with the holder's, so any model-level
feature reading its own journal would see it as always-empty in real deployment,
even though the exact same code works in a unit test that constructs the model
directly and calls a hand-rolled `attachActionLog` itself.
This surfaced building rung 4 (kanban) of the application ladder: `BoardModel`'s
activity-stream feature (design: derive from the framework journal via
`IActionLog::entries(entityKey)`, not a parallel table) needed exactly this, and
turned out to be the first model in the codebase attempting it.
Fix (already applied on the in-progress `ladder-kanban-impl` branch)
Added a `ModelLevelActionLogAttachable` concept (`include/morph/core/model.hpp`):
template <typename M>
concept ModelLevelActionLogAttachable = requires(M& model, std::shared_ptr<::morph::journal::IActionLog> log,
std::string key) {
{ model.attachActionLog(log, key) } -> std::same_as<void>;
};
`ModelHolder::onActionLogAttached` forwards to the wrapped model's own
`attachActionLog(log, contextKey)` iff the model declares one matching this
concept, gated by `if constexpr` — a no-op, unchanged compile path for every
model that doesn't declare one. This mirrors an already-established pattern in
the same file (`BackendChangedNotifiable`/`BackendChangedMixin`'s
`onBackendChanged()` hook), not a new idiom.
Verified via a codebase-wide grep that `kanban::BoardModel` is currently the
only model type declaring `attachActionLog` — every other model (bank,
bookmarks, pastebin, polls, and kanban's own `ProjectAdminModel`/`AuthModel`)
is structurally untouched by this change; the `if constexpr` branch simply
never instantiates for them.
An end-to-end test (dispatching through a real `wire::Envelope`/`RemoteServer`
path, not a hand-constructed model instance) confirms a registry-constructed
`BoardModel` now shares the same `IActionLog` the holder's own auto-append
writes to.
Ask
Backport this small, additive, opt-in framework hook to `master` independently
of kanban's own PR — it's a general-purpose seam any future model needing to
read back its own attached journal can use, not kanban-specific.
Problem
`IModelHolder`'s `attachActionLog`/`recordIfAttached`/`hasActionLog` operate
entirely on the holder wrapper — never on the wrapped model instance. This means
a model that itself wants to read back its own journal (e.g. to derive an
in-model feature like an activity stream from `IActionLog::entries(entityKey)`)
has no way to get at the same `IActionLog` the holder's own auto-append writes
entries into: `ModelFactory::create()` auto-attaches the process-wide
journal to the holder on every registry-constructed instance, and never calls
any model-level method.
Concretely: a registry-constructed model (the kind `RemoteServer`/`App` actually
dispatches through in production) has no mechanism to keep its own
`std::shared_ptr` in sync with the holder's, so any model-level
feature reading its own journal would see it as always-empty in real deployment,
even though the exact same code works in a unit test that constructs the model
directly and calls a hand-rolled `attachActionLog` itself.
This surfaced building rung 4 (kanban) of the application ladder: `BoardModel`'s
activity-stream feature (design: derive from the framework journal via
`IActionLog::entries(entityKey)`, not a parallel table) needed exactly this, and
turned out to be the first model in the codebase attempting it.
Fix (already applied on the in-progress `ladder-kanban-impl` branch)
Added a `ModelLevelActionLogAttachable` concept (`include/morph/core/model.hpp`):
`ModelHolder::onActionLogAttached` forwards to the wrapped model's own
`attachActionLog(log, contextKey)` iff the model declares one matching this
concept, gated by `if constexpr` — a no-op, unchanged compile path for every
model that doesn't declare one. This mirrors an already-established pattern in
the same file (`BackendChangedNotifiable`/`BackendChangedMixin`'s
`onBackendChanged()` hook), not a new idiom.
Verified via a codebase-wide grep that `kanban::BoardModel` is currently the
only model type declaring `attachActionLog` — every other model (bank,
bookmarks, pastebin, polls, and kanban's own `ProjectAdminModel`/`AuthModel`)
is structurally untouched by this change; the `if constexpr` branch simply
never instantiates for them.
An end-to-end test (dispatching through a real `wire::Envelope`/`RemoteServer`
path, not a hand-constructed model instance) confirms a registry-constructed
`BoardModel` now shares the same `IActionLog` the holder's own auto-append
writes to.
Ask
Backport this small, additive, opt-in framework hook to `master` independently
of kanban's own PR — it's a general-purpose seam any future model needing to
read back its own attached journal can use, not kanban-specific.