Skip to content

Ladder models that hand-roll journaling record unstamped entries, so #174's payload-shape check does not cover them #244

Description

@Yaraslaut

What is true

Four ladder models hand-roll their own journaling instead of going through the
framework's two execution sites, and construct morph::journal::LogEntry
directly:

  • examples/ledger/src/models/ledger_model.cpp (LedgerModel::logAction)
  • examples/ledger/src/models/budget_model.cpp (BudgetModel::logAction)
  • examples/ledger/src/models/rule_model.cpp (RuleModel::logAction)
  • examples/kanban/src/models/board_model.cpp (BoardModel::logAction)

The reason is documented and legitimate: morph appends a LogEntry
automatically only on the registry/dispatcher path
(IModelHolder::recordIfAttached), and a directly-constructed model — which is
how every ladder unit test builds one, per examples/IMPLEMENTATION.md rule 5 —
never goes through a holder. examples/lims/include/lims/core/self_journal.hpp
already says this in its file comment and exists to stop the copy count from
growing further.

Why it matters now

PR for #174 adds LogEntry::schema, a payload-shape fingerprint stamped at the
two framework execution sites (ActionDispatcher::registerAction's runner and
Bridge::executeVia's localOp) and checked by journal::replay(). An entry
with no fingerprint is unverifiable, and replay()'s default
UnstampedPayloadPolicy::Replay replays it exactly as before — i.e. a renamed
field still decodes to its default, silently.

So the rungs whose definition of done is "reconstructible from the journal
alone" are exactly the ones the new check does not cover, unless each
hand-rolled copy stamps the field. The one-line fix at each site is:

entry.schema = ::morph::model::detail::actionPayloadSchema<Action>();

Scope correction (was five sites, now four)

This issue originally also listed a fifth site,
examples/lims/src/models/analysis_catalog_model.cpp (AnalysisCatalogModel).
That listing is stale and has been dropped: AnalysisCatalogModel does not
hand-roll LogEntry at all — it calls
_journal.recordSuccess<AnalysisCatalogModel>(...) on a lims::SelfJournal
member (examples/lims/include/lims/models/analysis_catalog_model.hpp:64).

The fix landed for that call path in the exact same PR that filed this issue
(#247): entry.schema = ::morph::model::detail::actionPayloadSchema<Action>();
was added at examples/lims/include/lims/core/self_journal.hpp:169, confirmed
absent in the pre-merge parent commit and present after. SampleModel shares
the same SelfJournal member and picks up the same fix, so it was never a
distinct open site either.

The other four sites (ledger x3, kanban x1) hand-roll LogEntry directly
without going through an equivalent shared helper, so #247's fix — scoped to
SelfJournal — did not touch them. They remain unstamped.

Verification status

Reproduced for the mechanism, re-verified per site against current master.
Each of the five originally-cited sites has been read directly against the
tree that includes #247 (merged a615a6f8):

Site entry.schema stamped?
examples/ledger/src/models/ledger_model.cpp (LedgerModel::logAction) No
examples/ledger/src/models/budget_model.cpp (BudgetModel::logAction) No
examples/ledger/src/models/rule_model.cpp (RuleModel::logAction) No
examples/kanban/src/models/board_model.cpp (BoardModel::logAction) No
examples/lims/src/models/analysis_catalog_model.cpp (via SelfJournal) Yes (already fixed by #247)

Three of the four still-open sites (ledger/budget/rule) are textually
near-identical, down to copy-pasted comments ("See LedgerModel::logAction's
identical comment ..."). This is invariant-6 territory — a guarantee
reimplemented per call site belongs in one place — and it's already been named
once, in examples/lims/include/lims/core/self_journal.hpp's own file
comment, which lists these same sites as the copies it exists to stop
multiplying.

What would change the verdict

Close when every hand-rolled LogEntry construction in examples/** either
stamps schema or documents why it deliberately does not. Currently that
means the four sites above. Re-open if a new unstamped copy appears anywhere
in examples/**.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions