Task: Add tenant_read_scope: shared to badge_mapping_junction, regenerate all three badge repositories
Table of Contents
This page documents a task in the Fix cross-tenant badge lookup: stale generated repositories ignore tenant_read_scope: shared story. It captures the goal, current status, acceptance, and any notes or results.
Goal
Fix the cross-tenant badge lookup regression (see parent story
* Goal for the full root-cause writeup):
- Add
:tenant_read_scope: sharedtoores.dq.badge_mapping_junction.org's properties – it doesn't have the flag set at all. - Regenerate all three entities via codegen
(
compass codegen entity generate <entity> --address <repository facet's address>– check the archetype for the correct address, sincebadge_definition=/=badge_severityalready have the flag and their generated.cppis just stale). - Verify the regenerated diff carefully before accepting it: diff
the regenerated repository files line-by-line against current,
and if anything unrelated to the tenant-filter change shows up,
treat that as separate pre-existing drift to flag, not something
to silently accept – this exact trap hit a different generated
file (
iam_service_db_grants_create.sql) earlier this session, silently reverting unrelated hand-added grants never round-tripped back into the source.org. - Rebuild, recreate the DB, log in as a non-system tenant, confirm
badges render with real colours (not the orange fallback) and
ores.dq.service.0.logshowsTotal: 63=/=Total: 114(or current row counts) instead ofTotal: 0.
Scope split discovered mid-task
badge_mapping is a junction, not a domain_entity, and its
repository template turned out to have no
\{\{read_tenant_filtered\}\} support at all (see below) – fixing
it properly means template surgery affecting every junction model,
not a model-only change. Split into
its own follow-up task;
this task's scope is now badge_definition=/=badge_severity only.
At the time, the :tenant_read_scope: shared flag was also added to
badge_mapping_junction.org (harmless, currently a no-op) and
core.py was extended to correctly compute
junction['read_tenant_filtered'] (verified via a direct Python
harness) as groundwork for the follow-up task. Both were later
reverted, alongside the sibling tenant_read_scope: shared revert
below, once the follow-up task itself was abandoned – see
** Post-rebase live QA reversed the fix.
Status
| Field | Value |
|---|---|
| State | DONE |
| Parent story | Fix cross-tenant badge lookup: stale generated repositories ignore tenant_read_scope: shared |
| Now | Nothing. |
| Waiting on | Nothing. |
| Next | Nothing. |
| Last touched | 2026-07-30 |
Acceptance
Superseded mid-task by live QA findings – see * Result for what
actually shipped. Original acceptance criteria (below) are struck
through as no longer the goal:
[ ]=ores.dq.badge_mapping_junction.org= declares =:tenant_read_scope: shared=– reverted; junction's shared-read case no longer exists (see story* Decisions).[ ]Regenerated =badge_definition_repository.cpp=, =badge_severity_repository.cpp= no longer hard-code =tenant_id = ctx.tenant_id()= on read methods– reverted back to tenant-filtered reads after live QA found duplicate-row regressions.[X]Regenerated-diff review found no unrelated drift for either entity'sores.cpp.repository=/=ores.sql.schemafacets; the natural-key migration needed forbadge_definition(movingnameintoColumnswith:natural_key: true, discovered when the first migration attempt silently dropped the column from the generated entity/mapper) is documented in* Planrather than silently folded in. – Still true; this part of the work stands.[X]Verified live as a non-system tenant via the QA Validation Runner (scenario_verify-badge-tenant-scoping.org): confirmed the original symptom no longer reproduces (bundle-publish gives each tenant its own copy), and confirmed the RLS-fallback fix's duplicate-row regression before deciding to revert it.
Plan
Turned out to be three layers deeper than the story anticipated:
- Primary-key model shape.
badge_definition.org=/=badge_severity.orgdeclared their primary key via the legacy* Primary keyheading, which the currentorg_loader.pyno longer reads at all –validate_model()only derivesprimary_keyfrom a:primary_key: trueflag inside* Columns. Confirmed this is repo-wide pre-existing breakage, not badge-specific: regeneratingcatalog(unrelated, also un-migrated) hits the identical validation error. ~61 of 120ores.codegen.entitymodels are still on this legacy shape and can't be regenerated via codegen today. Migrated justbadge_definition=/=badge_severityto the new shape (mirroringores.dq.subject_area.org, the one fully- migrated exemplar in this component), rather than attempt the wider repo-wide migration – out of scope here. - Natural-key model shape (same category, easy to miss). The
first migration attempt moved only the primary key and left
nameunder the old* Natural keysheading. That heading is also dead in the current loader – natural keys need:natural_key: trueinside* Columnstoo. Regenerating with only the primary key migrated silently droppednamefrom the generated entity/mapper entirely (a real data-loss bug, caught by diffing before applying, never applied). Migrated both key roles together. - Generator-suffix double-application. Copied the checked-in
generator's
+ "-" + std::to_string(idx)suffix verbatim into the migrated model's:primary_key:generator block; the codegen generator template already appends that suffix automatically for text primary keys (and, separately, for text natural keys) – copying it produced a doubled suffix in the regenerated generator.cpp. Caught via the full (non-address-restricted) diff; fixed by providing only the base expression.
badge_definition=/=badge_severity regenerated at
ores.cpp.repository (clean diff: tenant-filter removal + harmless
template-formatting cosmetics only) and ores.sql.schema (zero
diff). Did not run the default full-address regeneration for these
two entities in this task: it also touches Qt/service/generator
layers where separate, genuinely unrelated pre-existing drift
surfaced (e.g. a hand-added guard in
BadgeDefinitionDetailDialog.cpp against deleting the reserved
__unmapped__ fallback badge would be silently dropped) – flagged
as a distinct pre-existing issue, not touched here, since neither is
related to the tenant-filter fix.
badge_mapping (junction) is the third layer – see * Notes and
the split-off follow-up task (later abandoned, see below).
Post-rebase live QA reversed the fix
After rebasing onto latest main (283 commits landed while this
task was in flight) and rebuilding, live QA via the
* Test Scenarios entry below found:
- The originally-reported symptom (
Total: 0, fallback-colour badges) no longer reproduces on a freshly provisioned tenant – confirmed via direct SQL that Barclays Plc now has its own copiedbadge_definition=/=badge_severity=/=badge_mappingrows (an independent upstream provisioning change: thebasebundle now publishes these datasets per-tenant). - With both the tenant's own rows and (via our
tenant_read_scope: sharedfix) the system-tenant rows visible through RLS's own-OR-system fallback, the Badge Definitions/Badge Severities admin lists show every code duplicated – a new regression, not present before this task's fix.
Reverted tenant_read_scope: shared from both .org models,
regenerated badge_definition=/=badge_severity back to their
original tenant-filtered reads (diff against origin/main for the
repository .cpp=/.hpp= files is now an exact match, modulo harmless
template-formatting cosmetics from the kept model-shape migration),
and reverted the core.py junction read_tenant_filtered
computation added to support the abandoned follow-up task. Rebuilt
ores.dq.core.lib and reran ores.dq.core.tests=/
=ores.dq.api.tests=/=ores.dq.service.tests – all pass.
Notes
Flagged, not fixed here (out of scope for this task):
- A full (non-
--address-restricted) regeneration ofbadge_definitionshows unrelated pre-existing drift inBadgeDefinitionDetailDialog.cpp(a hand-added guard preventing deletion of the reserved__unmapped__fallback badge would be silently reverted) andClientBadgeDefinitionModel.cpp=/ =BadgeDefinitionDetailDialog.ui(Qt-layer template evolution since these were last regenerated – tooltip role handling, colour-button placeholder text). None of this is related to the tenant-filter fix; worth a separate capture if the Qt layer for these entities is ever intentionally regenerated. - ~59 other
ores.codegen.entitymodels still on the legacy* Primary key=/=* Natural keysheading shape are equally unable to regenerate via codegen today. Worth a backlog capture to track the wider migration, since this task only touched the two badge entities.
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 |
|---|---|---|
| Verify tenant-scoped badge catalogue (severities, definitions, code domains, mappings) | FAILED (as expected, drove the revert) | Reused an existing sprint_23 scenario (closest match, describes the per-tenant-copy design that turned out to already be in place). Steps 2-3 caught the duplicate-row regression from this task's now-reverted fix; the scenario's own * Results records the run. |
PRs
| PR | Title |
|---|---|
| #1761 | [ores.dq] Migrate badge model shape; revert RLS-fallback fix |
Review
| # | Comment summary | File | Decision | Notes |
|---|---|---|---|---|
| 1 | "Scope split discovered mid-task" section still reads as if tenant_read_scope: shared were still in place on badge_mapping_junction.org, contradicting the later revert record | task_regenerate-badge-repositories-with-tenant-read-scope.org | Fixed | Cross-referenced the later "Post-rebase live QA reversed the fix" section that records the revert. |
| 2 | Stray double-blank-lines in generated repository .hpp/.cpp files | badge_definition_repository.hpp/.cpp, badge_severity_repository.hpp/.cpp | Declined | Codegen template formatting artifact, not hand-introduced; matches other regenerated repositories in the codebase. |
| 3 | New backlog captures have empty "See also" sections | fix_badge_severity_combo_duplicates.org, migrate_legacy_primary_key_shape_entities.org | Declined | Intentional – neither has a directly related knowledge doc/story to link yet; the References sections already carry the relevant pointers. |
Result
Net change from this task: badge_definition.org/badge_severity.org migrated to the current Columns + :primary_key:/:natural_key: model shape (a real, harmless prerequisite fix, unblocking any future regeneration of these two entities); everything else reverted.
The originally-reported cross-tenant badge bug turned out to already
be fixed on current main by an independent, unrelated upstream
change (tenant provisioning now bundle-publishes badge data per
tenant). Applying the tenant_read_scope: shared fix on top of that
caused a new regression (duplicate rows in the Badge
Definitions/Severities admin lists), caught via live QA and reverted
before merge. No functional code change ships from this task; the
model-shape migration is the only lasting artefact, plus two
backlog-worthy findings recorded in the story's * Out of scope
(wider legacy-model-shape migration; Qt severity-combo duplicate/
non-badge rendering).