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 for calendar_dates, scoped by calendar_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_dates has no messaging/NATS layer generated yet, so there is nothing to point the Qt list at. Deferred to whichever task wires calendar_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.mustache declares openEdit=/=openHistory unconditionally (only the override keyword is gated, by has_explorer_interface) – there is no knob that removes them.
  • cpp_qt_mdi_window.cpp.mustache wires 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 existing has_csv_xml_io knob – 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.mustache gates request.offset=/=limit and the total_available_count plumbing behind the existing has_pagination knob (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 any cpp.qt template – parent-scoping a list window (e.g. by calendar_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_io on/off pattern already proven in cpp_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_dates scoped by calendar_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.

Emacs 29.3 (Org mode 9.6.15)