Task: Codegen the history-provider registrar wiring
Table of Contents
This page documents a task in the Close systemic codegen gaps story. It captures the goal, current status, acceptance, and any notes or results.
1. Goal
projects/ores.refdata/core/src/messaging/registrar.cpp has grown
into a single monolithic hand-maintained file registering NATS CRUD
handlers, per-entity registrars, and (after wiring history providers
for the entities migrated in task AA97E310) history providers for
every refdata entity — 100+ #include lines and dozens of
near-identical register_history_provider() lambda blocks, differing
only by entity name, service method name (get_<x>_history, with
several entity-specific irregular spellings like get_type_history
or get_centre_history), and mapper function name
(render_<entity>_fields). This is a clear codegen candidate: a
per-entity archetype emitting the include/registration snippet
(matching the entity_type_of()=/history-field-mapper pattern already
codegen'd), assembled by the registrar the same way per-entity
=*_registrar.hpp handler registrations already are, rather than
continuing to hand-edit one giant file for every entity that migrates
onto the generic HistoryDialog.
2. Status
| Field | Value |
|---|---|
| State | DONE |
| Parent story | Close systemic codegen gaps |
| Now | Nothing. |
| Waiting on | Nothing. |
| Next | Nothing. |
| Last touched | 2026-09-04 |
3. Acceptance
- New facet
ores.cpp.history-provider-registrarwith two archetypes (_header/_implementation), mirroring theores.cpp.nats-event-registrarfacet's conventions: facet and archetype modelling docs inprojects/ores.codegen/library/templates, a mustache template pair in the same directory, and registration in the entity meta-model documentation. - The archetypes emit, for every refdata entity that already receives
a history field mapper (the 64-entity presentation-facet set), one
<entity>_history_provider_registrar.hpp/.cpppair inores.refdata.core/messagingdeclaringregister_<entity>_history_provider(ores::history::service::dispatch_registry&). The implementation wraps the exact provider body today'sregistrar.cppblocks carry: the"ores.refdata.<entity_singular>"entity-type key,service::<entity_singular>_serviceconstruction from the scoped context,get_<entity_singular_short>_historydispatch, andpresentation::render_<entity_singular>_fieldsthroughores::history::service::build_entity_history_versions. All service/mapper/domain includes move into the generated file. registrar.cppis reduced to the assembly role: the ~480-line provider region becomes 53register_<entity>_history_providercalls inside the existinghistory_registry()scope, with the per-entity registrar headers added to the include block. The registered key set is byte-identical to today's (53 keys).- Verification: emission count
= generated mapper count (64 registrar pairs); registered-key parity between the rewritten =registrar.cppand the pre-change block list (53 keys, no additions or removals); full build and ctest green; codegen drift checks green; codegen pytest suite green.
4. Plan
Option A — per-entity archetype pair, hand-written assembler.
Rejected alternative: a whole-file registrar.cpp template. The
file is a true assembler (NATS wiring, queue groups, the
function-local history_registry() static, handler stitching), not a
per-entity shape; templating it would force every future structural
change through mustache for no per-entity gain. The per-entity
registrar, by contrast, is the same shape the codegen program
already ships for the NATS sub-registrars (register_<entity>_handlers)
and the history field mappers, and it removes the whole
"hand-edit the giant file per migrated entity" failure mode:
the 11 mapper-bearing entities with no provider today stay
unregistered (identical runtime behaviour), but the act of adding
one is a model property change plus regeneration, never a hand edit
of registrar.cpp.
Implementation order:
- Trace the presentation facet's activation path end to end on one
wired entity (e.g.
currency), so the new archetypes gate on the same signal that emits the 64 mappers. - Modelling docs: facet + two archetype docs with fresh UUIDs under
projects/ores.codegen/library/templates, tangle them viacompass build --direct codegen_templates, and register the facet inentity_meta_model_cpp_presentation.org(or the meta-model hub if a new facet segment is warranted). - Mustache pair
cpp_history_provider_registrar.hpp/.cpp.mustache: model inputs areentity_singular,entity_singular_short,product,component(all already on the model; the short form already drives the irregularget_type_history=/=get_centre_historyspellings on the generated services). - Regenerate refdata (
compass codegen regenerate), regenerate the cmake component file lists, then rewriteregistrar.cpp's history region: remove the 53 lambda blocks and the service/domain/mapper includes they need, add the 53 registrar-header includes, and emit the 53 calls in alphabetical order (matching the current block order) inside the existinghistory_registry()scope. - Enumerate the census before the rewrite and diff it after: mapper
glob (64), pre-change provider keys (53) captured from the block
list, post-change call keys (53). The 11 unwired mapper entities
are named in the
* Resultfor the record. - Local checks per the python-tooling/code rule table: codegen pytest, compass pytest, full build + ctest, drift checks.
- Agile close before PR raise (this doc's
* Result, story* Decisionsdistillation, sprint/story row sync), thencompass pr createwith the change class recorded.
5. Notes
5.1. Activation path (plan step 1, traced 2026-09-04)
The history-field-mapper set (64) is not a per-entity opt-in anywhere:
no refdata org mentions ores.cpp.presentation. It is pure physical
space admission — the ores.cpp.presentation facet doc's
#+model_types: schema domain_entity with a default-enabled facet
admits every domain_entity org of a component, and refdata has
exactly 64 domain_entity orgs (74 orgs total = 1 component + 9
junctions + 64 entities). The new facet gates the same way:
#+model_types: domain_entity, default-enabled, #+facet_group:
ores.cpp ⇒ emission == mapper set by construction, for refdata and
for any component that regenerates later.
A full census of the 53 register_history_provider blocks (captured
in /tmp/history_provider_census.json before any rewrite) proves each
block is fully determined by the entity org: provider key =
{{product}}.{{component}}.{{entity_singular}} (same composition the
notify-trigger SQL uses for its entity_name), service class
{{entity_singular}}_service, service method
get_{{entity_singular_short}}_history (the entity_singular_short
Repository-drawer property, hoisted to the model top level, drives the
irregular spellings get_type_history=/=get_centre_history), mapper
function presentation::render_{{entity_singular}}_fields. All 53
blocks check out against the org properties; all 64 services declare
their short-named history method, so the 64 generated pairs compile
even for the 11 unwired entities.
6. Test Scenarios
Manual QA scenarios (scaffolded via compass add test_scenario, run
through the QA Validation Runner panel) that verify this task. Link
new ones here as they're created; the scenario doc itself links back
via its "Verifies task" field.
| Scenario | State | Notes |
|---|---|---|
7. PRs
| PR | Title |
|---|---|
| #2010 | [ores.refdata] Codegen the history-provider registrar wiring |
8. Review
| Comment summary | File | Decision | Notes |
|---|---|---|---|
9. Result
- The
ores.cpp.history-provider-registrarfacet exists with the two archetypes (_header/_implementation) inprojects/ores.codegen/library/templates, the mustache pair tangled byte-identically, and the facet registered in the C++ presentation segment of the entity meta-model hub (the hub row now lists both facets). The facet doc states the drift rationale: refdata's registrar registers only 53 of its 64 mapper-bearing entities, and a compile failure in one entity's provider must not block the rest. - The archetypes emit one
<entity>_history_provider_registrar.hpp/.cpppair per domain entity for every component that regenerates: refdata now carries 64 pairs, one per mapper-bearing entity (= the mapper glob count). Each implementation wraps the exact provider body registrar.cpp used to hand-carry: the =product.component.entity_singularkey (composed identically to the notify-trigger SQLentity_name), the service read viaget_<entity_singular_short>_history(irregular spellings included), and the presentation mapper render throughbuild_entity_history_versions(). The component file lists were regenerated (+128 lines, registrar entries only). registrar.cppis reduced to the assembly role (961 lines to 449): the ~480-line provider region is 53register_<entity>_history_provider(hist_registry)calls in the existinghistory_registry()scope, in alphabetical order matching the new registrar-header include block above it. The 148 domain/mapper/service/version_builder includes the blocks needed moved into the generated files. The registered key set is byte-identical: post-rewrite call keys match the pre-change census line for line (53 keys). The 11 mapper-bearing entities with no provider today (calendar_exception, calendar_rule, calendar_type, counterparty_contact_information, counterparty_identifier, derivation_kind, instrument_code, ir_curve_bootstrap_config, ir_curve_bootstrap_pillar, party_contact_information, party_identifier) stay unregistered with identical runtime behaviour; wiring one is now a model change plus regeneration plus one call line, never a hand edit of the giant file.- Verification: codegen pytest 155 passed; compass pytest 117 passed,
1 skipped; roundtrip exit 0; cmake-sources drift refdata-clean (11
pre-existing stale lists in other components recorded for the
per-component drift stories); codegen drift green (tangle +
check_component_drift.pyexit 0, zero tracked diffs); site build clean; full build green (linux-clang-debug-make, zero errors); ctest 71/71 passed (883s; ores.refdata.core.tests 261.6s); CDash Experimental green: serial rerun 71/71 passed and submitted (20260904-1925). The first parallel attempt aborted on duplicate-key rows inores.iam.core.testsleft open in the shared test DB by an earlier aborted run — an environmental collision in a suite this diff does not touch, not a regression.
10. Promoted from capture
Captured 2026-07-15 in the product backlog; promoted preserving the UUID.
10.1. What
projects/ores.refdata/core/src/messaging/registrar.cpp has grown
into a single monolithic hand-maintained file registering NATS CRUD
handlers, per-entity registrars, and (after wiring history providers
for the entities migrated in task AA97E310) history providers for
every refdata entity — 100+ #include lines and dozens of
near-identical register_history_provider() lambda blocks, differing
only by entity name, service method name (get_<x>_history, with
several entity-specific irregular spellings like get_type_history
or get_centre_history), and mapper function name
(render_<entity>_fields). This is a clear codegen candidate: a
per-entity archetype emitting the include/registration snippet
(matching the entity_type_of()=/history-field-mapper pattern already
codegen'd), assembled by the registrar the same way per-entity
=*_registrar.hpp handler registrations already are, rather than
continuing to hand-edit one giant file for every entity that migrates
onto the generic HistoryDialog.
10.2. Why
Found while wiring server-side history providers for the entities migrated in Delete per-entity history-dialog codegen archetypes and reconcile dynamic_combo drift: client-side controller migration and server-side history-provider registration turned out to be two separate, easy-to-miss steps (a migrated entity compiles and opens its History window fine, but shows "No history provider registered for entity_type: …" until this file is hand-updated too). As more entities migrate, this file keeps growing linearly by hand — exactly the kind of repetitive, entity-parameterised boilerplate the rest of this story already codegens (client model, controller, detail dialog, history-field mapper). Codegen'ing this registration removes both the manual toil and the "forgot to wire the server side" failure mode entirely.
10.3. References
projects/ores.refdata/core/src/messaging/registrar.cpp— the file in question.projects/ores.refdata/core/include/ores.refdata.core/presentation/*_history_field_mapper.hpp— the sibling codegen'd artefact this registration consumes.