Task: Add codegen Qt facet for paginated read-only junction-scoped lists
Table of Contents
This page documents a task in the Calendar entity follow-ups: date picker, list-pagination fix, QuantLib materialization story. It captures the goal, current status, acceptance, and any notes or results.
Goal
Every existing junction entity in this codebase (currency_calendar,
currency_pair_convention_calendar, currency_country, …) has zero Qt
files: the established pattern for a many-to-many relationship
junction is a hand-written widget embedded as a tab in the owning
entity's own detail dialog (e.g. Currency's CalendarAssignmentWidget
tab), not a standalone list window. That pattern is correct for small,
user-assigned relationships and is not what this task changes.
calendar_dates
(ores.refdata.calendar_dates) doesn't fit that mould: it is a large
(potentially thousands of rows per calendar, spanning a decade-plus),
read-only, server-paginated materialised fact table, not a relationship
a user assigns. Its shape is closer to the existing History dialog
facet (read-only, scoped/filtered by a parent key) than to the
assignment-widget pattern – except History dialogs load everything at
once (a version history is small), while this shape genuinely needs
server-side pagination.
This task adds that missing shape to ores.codegen's Qt facet: a
client model + list dialog for a junction-backed, read-only,
paginated, parent-scoped dataset – no detail dialog, since there is
nothing to edit. Generated for calendar_dates as the first consumer,
but written generically so any future entity in this shape (junction
or otherwise) can opt into it.
Status
| Field | Value |
|---|---|
| State | DONE |
| Parent story | Calendar entity follow-ups: date picker, list-pagination fix, QuantLib materialization |
| Now | Nothing. |
| Waiting on | Nothing. |
| Next | Nothing. |
| Last touched | 2026-07-25 |
Acceptance
[X]A new Qt codegen knob/shape exists for a read-only, server-paginated, parent-scoped list (client model + list dialog), distinct from the existing full-CRUD domain_entity shape and from the read-only-but-unpaginated History dialog shape.[ ]Generating it forcalendar_dates, scoped bycalendar_code, produces a working paginated list (offset/limit sent to the server, accurate total count – respecting the fix from Fix silent 100-row list truncation on entities missing has_pagination) with no add/edit/delete affordances anywhere in the generated UI. Not met – blocked, see Result:calendar_dateshas no messaging/NATS layer generated yet, so there is nothing to point the Qt list at. Deferred to whichever task wirescalendar_dates's Qt drawer once that layer exists (parent-scoping wiring is also still undesigned).[X]Documented in the Qt facet's meta-model page (entity_meta_model_cpp_qt.org) alongside the existing knobs, so the next entity in this shape doesn't have to rediscover it.
Plan
(Implementation strategy. Written when work starts; key decisions
are distilled into the parent story's * Decisions at close, but the
plan itself stays — it is the historical record of what we did.)
Investigation findings
The ores.cpp.qt facet has six mustache templates:
cpp_qt_client_model.{hpp,cpp}, cpp_qt_controller.{hpp,cpp},
cpp_qt_detail_dialog.{hpp,cpp,ui}, cpp_qt_mdi_window.{hpp,cpp}.
None of them has a way to omit CRUD:
cpp_qt_controller.hpp.mustachedeclaresopenEdit=/=openHistoryunconditionally (only theoverridekeyword is gated, byhas_explorer_interface) – there is no knob that removes them.cpp_qt_mdi_window.cpp.mustachewires up Add/Edit/Delete/History toolbar actions and their slots unconditionally. By contrast, Import XML/Export CSV/Export XML are already optional, gated by the existinghas_csv_xml_ioknob – proof the template style supports this kind of on/off toggle when someone adds one.- Pagination itself is already fully generic and reusable as-is:
cpp_qt_client_model.cpp.mustachegatesrequest.offset=/=limitand thetotal_available_countplumbing behind the existinghas_paginationknob (implemented for the truncation-fix task, 914B206F). Nothing new needed there. :parent_entity_singular:=/:parent_entity_pascal:= are documented in the meta-model page and used by the doc-generation facet, but are not currently read by anycpp.qttemplate – parent-scoping a list window (e.g. bycalendar_code) is itself new wiring, not an existing knob this task can just flip on.- No detail dialog is needed for this shape at all: the
cpp_qt_detail_dialog.*templates are full-CRUD-only (create/edit form + save/delete). A read-only list has nothing to edit, so those three templates simply aren't invoked for this shape.
Direction
Add a new boolean knob, tentatively :has_readonly_paginated_list:,
that when true:
- suppresses generation of the detail dialog trio entirely;
- in the controller, drops
openEdit=/=openHistory=/openNew/openDelete wiring (mirrors the =has_csv_xml_ioon/off pattern already proven incpp_qt_mdi_window.cpp.mustache); - in the mdi_window, drops the Add/Edit/Delete/History toolbar actions and their slots, keeping only Reload (and CSV/XML export, if separately requested) plus the parent-scoping wiring below;
- wires a new
:parent_key_field:=/:parent_key_param:=-style knob so the list's get-request is scoped by a parent id/code (first real use of parent-scoping in this facet –calendar_datesscoped bycalendar_code), reusing the already-generic pagination knob for the server round trip.
Next steps: draft the exact knob names/shapes against
entity_meta_model_cpp_qt.org, then prototype the templates against
calendar_dates as the first consumer.
Implemented
:has_readonly_paginated_list: (default false) landed across
controller_header=/=controller_impl=/=mdi_window_header=/
=mdi_window_impl, gated with ...{{^has_readonly_paginated_list}}
sections mirroring the existing has_csv_xml_io on/off pattern.
When set: no detail-dialog wiring (showAddWindow=/=showDetailWindow=/
=openAdd=/=openEdit), no history (showHistoryWindow=/=openHistory=/
version navigation/revert), no Add/Edit/Delete/History toolbar actions
or slots. Reload and =:has_pagination: pagination are untouched –
orthogonal knobs. Documented in
entity_meta_model_cpp_qt.org's Behavioural knob group.
Verification: retangled, then diffed two structurally different
existing entities' generated Qt output against HEAD via
compass codegen entity generate <entity> --address ores.cpp.qt --diff
with the knob left unset (its default): book (UUID PK, parent
relationship, change-reason cache, badges) and currency (text PK,
CSV/XML import/export). Both came back clean of any diff attributable
to this change – book's diff was pure pre-existing drift in
client_model=/=detail_dialog templates this task didn't touch (a
stale checked-in generated file vs. current templates, unrelated to
this task); currency diffed byte-identical.
Not done – blocked: the acceptance criterion "generating it for
calendar_dates produces a working paginated list" is blocked.
compass codegen entity show calendar_date shows only
domain/SQL/repository layers generated so far – no
ores.cpp.protocol=/=ores.cpp.nats-handler facets have been run, so
there is no get_calendar_dates_request=/=response NATS message pair
to point the Qt list's :get_request_class:=/:get_message_type:= at.
Wiring the Qt drawer onto calendar_date.org needs that messaging
layer generated first – out of this task's scope (codegen template
work only). The parent-scoping wiring
(:parent_key_field:-style knob binding the list's get-request to a
parent id/code) described in Direction above is also still
undesigned/unimplemented, deferred to whichever task actually wires
calendar_dates's Qt drawer once the messaging layer exists.
Notes
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 |
|---|---|---|
PRs
| PR | Title |
|---|---|
| #1719 | [codegen] Add has_parent_scoped_list Qt knob for parent-scoped lists |
Review
| Comment summary | File | Decision | Notes |
|---|---|---|---|
Result
Added :has_readonly_paginated_list: to ores.codegen's
ores.cpp.qt facet: a boolean knob that strips every add/edit/
delete/history affordance (toolbar actions and slots, detail-dialog
wiring, version navigation, revert) from a generated controller and
MDI window, leaving Reload and the existing :has_pagination:
plumbing untouched. Landed across
controller_header=/=controller_impl=/=mdi_window_header=/
=mdi_window_impl.org, following the on/off section pattern already
proven by :has_csv_xml_io:. Documented in
entity_meta_model_cpp_qt.org. Verified by regenerating book and
currency (with the knob at its default, unset) via
--address ores.cpp.qt --diff and confirming zero drift attributable
to this change.
Two of three acceptance items met; the third (generating a working
paginated list for calendar_dates itself) is blocked on
calendar_dates not yet having a messaging/NATS layer generated, and
on the still-undesigned parent-scoping knob – see Plan > Implemented
for the full account. Closing this task as the codegen-template piece
is complete and self-contained; the remaining wiring work belongs to
a follow-on task once the messaging layer for calendar_dates exists.