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/**.
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::LogEntrydirectly:
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
LogEntryautomatically only on the registry/dispatcher path
(
IModelHolder::recordIfAttached), and a directly-constructed model — which ishow every ladder unit test builds one, per
examples/IMPLEMENTATION.mdrule 5 —never goes through a holder.
examples/lims/include/lims/core/self_journal.hppalready 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 thetwo framework execution sites (
ActionDispatcher::registerAction's runner andBridge::executeVia'slocalOp) and checked byjournal::replay(). An entrywith no fingerprint is unverifiable, and
replay()'s defaultUnstampedPayloadPolicy::Replayreplays it exactly as before — i.e. a renamedfield 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:
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:
AnalysisCatalogModeldoes nothand-roll
LogEntryat all — it calls_journal.recordSuccess<AnalysisCatalogModel>(...)on alims::SelfJournalmember (
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, confirmedabsent in the pre-merge parent commit and present after.
SampleModelsharesthe same
SelfJournalmember and picks up the same fix, so it was never adistinct open site either.
The other four sites (ledger x3, kanban x1) hand-roll
LogEntrydirectlywithout 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):entry.schemastamped?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)examples/lims/src/models/analysis_catalog_model.cpp(viaSelfJournal)Three of the four still-open sites (
ledger/budget/rule) are textuallynear-identical, down to copy-pasted comments ("See
LedgerModel::logAction'sidentical 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 filecomment, which lists these same sites as the copies it exists to stop
multiplying.
What would change the verdict
Close when every hand-rolled
LogEntryconstruction inexamples/**eitherstamps
schemaor documents why it deliberately does not. Currently thatmeans the four sites above. Re-open if a new unstamped copy appears anywhere
in
examples/**.