Story: Fix cross-tenant badge lookup: stale generated repositories ignore tenant_read_scope: shared
Table of Contents
This page documents a story in Sprint 24. It captures the goal, current status, acceptance criteria, and the tasks that compose it.
Goal
Found by brave_hopper: every badge in the UI renders with the default/fallback colour, for every entity, in every tenant except the system tenant.
Root cause: badge_definition_repository.cpp and
badge_mapping_repository.cpp (ores.dq.core) hard-code an app-level
WHERE tenant_id = ctx.tenant_id() filter on every read method –
narrower than the RLS policy on both tables, which already permits:
using (tenant_id = ores_iam_current_tenant_id_fn() OR tenant_id = ores_utility_system_tenant_id_fn())
Badge definitions/mappings are seeded once under the system tenant
(ffffffff-ffff-ffff-ffff-ffffffffffff) as shared reference data
meant to be visible to every tenant. RLS would let the system-tenant
rows through; the repository's own narrower WHERE clause silently
returns zero rows first.
Verified live: ores_dq_badge_definitions_tbl=/=ores_dq_badge_mappings_tbl
have 63/114 rows, all tenant_id = ffffffff-.... BadgeCache on login
logs "Fetched 0 badge definitions" / "Fetched 0 badge mappings" for a
non-system tenant; server-side, ores.dq.service's own log shows
"Reading latest badge definitions… Total: 0".
The fix mechanism already exists, just not applied consistently: codegen
has a tenant_read_scope: shared entity-model flag
(ores.codegen/src/codegen/core.py, search read_tenant_filtered)
specifically built to omit the tenant filter on reads for exactly this
class of table.
ores.dq.badge_definition.org– hastenant_read_scope: sharedalready, but the generated.cppis stale, never regenerated since the flag was added.ores.dq.badge_severity.org– same flag, same staleness; not directly verified live but the generated repository has the same hard-coded filter.ores.dq.badge_mapping_junction.org– does not have the flag set at all.
Status
| Field | Value |
|---|---|
| State | DONE |
| Parent sprint | Sprint 24 |
| Now | Nothing. |
| Waiting on | Nothing. |
| Next | Nothing. |
| Last touched | 2026-07-30 |
Acceptance
[X]Confirmed live whether the reported symptom (badges rendering with the fallback colour for non-system tenants) still reproduces on currentmain. It does not: tenant provisioning was updated upstream (independently of this story) to publish each tenant's own copy of the badge catalogue via the standard DQ bundle-publish mechanism, so every tenant now has its ownbadge_definition=/ =badge_severity=/=badge_mappingrows and never depends on the system-tenant RLS fallback this story set out to fix.[X]tenant_read_scope: sharedreverted forbadge_definition=/ =badge_severityafter live QA (see* Decisions) found it causes a new regression given the above: a tenant now has both its own copied rows and (via RLS's own-OR-system-tenant fallback) the original system-tenant rows visible simultaneously, so admin list views (Badge Definitions, Badge Severities) show duplicates with no dedup.
Tasks
| Task | State | Start | End | Description |
|---|---|---|---|---|
| Add tenant_read_scope: shared to badge_mapping_junction, regenerate all three badge repositories | DONE | 2026-07-29 | 2026-07-30 | Investigated, fixed, then reverted after live QA found a regression given an independent upstream provisioning change – see task Result. |
| Support tenant_read_scope: shared in the junction repository template | ABANDONED | Motivating case (badge_mapping) no longer needs shared-tenant reads: tenants now get their own copy via bundle-publish. No other junction currently needs this. |
Decisions
Reverted tenant_read_scope: shared for badge_definition/badge_severity
Live QA (doc/agile/versions/v0/sprint_23/badge-colour-scheme-support/scenario_verify-badge-tenant-scoping.org)
against a freshly rebased, freshly provisioned Barclays Plc tenant
found the originally-reported symptom (Total: 0, badges rendering
with the fallback colour) no longer reproduces: tenant provisioning
now publishes the base bundle's ore.badge_definitions=/
=ore.badge_severities=/=ore.badge_mappings datasets into each
tenant's own rows (confirmed via direct SQL: Barclays' tenant has
its own 62/116 rows alongside the system tenant's 63/114). This
landed independently, upstream, sometime in the ~283 commits between
when this story was filed and when its fix was live-tested.
Given that, tenant_read_scope: shared's RLS-fallback mechanism is
no longer the operative fix for these two entities – and applying
it anyway is actively harmful: with a tenant now holding its own
copy and RLS still permitting the system-tenant row through,
read_latest() (no dedup, no natural-key tie-break) returns both,
so every code shows up twice in the Badge Definitions/Severities
admin lists. Verified live via the QA scenario above (steps 2-3
FAILED with exactly this symptom before the revert).
Reverted the tenant_read_scope: shared flag and regenerated both
repositories back to their original tenant-filtered reads (diff
against origin/main is a clean, exact match modulo the harmless
model-shape migration below). Also reverted the
core.py junction read_tenant_filtered computation added to
support the (now-abandoned) follow-up task – no other junction
currently has a motivating use case for it.
Kept: primary-key/natural-key model-shape migration
Unrelated prerequisite discovered along the way, independent of the
tenant-scope question: badge_definition.org=/=badge_severity.org
were on the legacy * Primary key=/=* Natural keys heading shape,
which the current org_loader.py no longer reads at all –
confirmed repo-wide (~61 of 120 ores.codegen.entity models are in
the same state; regenerating any of them fails the same validation).
Migrated just these two to the Columns + :primary_key:=/
=:natural_key: true shape (mirroring ores.dq.subject_area.org),
since it's a real, harmless fix that unblocks any future
regeneration of these two entities regardless of what happens with
tenant scoping. The wider repo-wide migration is out of scope here
(see * Out of scope).
Out of scope
- The wider migration of ~59 other
ores.codegen.entitymodels still on the legacy primary-key/natural-key heading shape. Filed: Migrate remaining entities off the legacy Primary key/Natural keys heading shape. - Two Qt-layer UI polish items noticed during live QA, unrelated to
tenant scoping: the
Badge Definitiondetail dialog's Severity combo box shows duplicate entries and doesn't render as badges. Filed: Fix badge severity combo box duplicates and non-badge rendering.