Task: Scope the configuration documents to a party
Table of Contents
This page documents a task in the Model the ORE report configuration as entities story. It captures the goal, current status, acceptance, and any notes or results.
1. Goal
A party sees and writes only its own configuration documents, as it already does its report definitions, and two parties in one tenant can each hold a document with the same names.
2. Status
| Field | Value |
|---|---|
| State | DONE |
| Parent story | Model the ORE report configuration as entities |
| Now | Nothing. |
| Waiting on | Nothing. |
| Next | Nothing. |
| Last touched | 2026-10-03 |
3. Acceptance
- Every table behind the run document, the reporting registry, pricing engines, today's market, stress test, credit simulation, conventions and curve configuration has party_id and a restrictive party RLS policy.
- A test per document group proves a party cannot read another party's rows.
- Two parties can each import the same corpus document.
- Currencies, currency pairs and calendar adjustments stay per tenant.
- World conventions (IBOR index, overnight index, currency pair) stay per tenant; the instrument conventions are per party.
4. 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.)
5. Measurements
Every number this task claims about a tree, with the command that produced it
and the commit it was measured at. A number without both is not a measurement,
because a clean-up record describes a tree and trunk moves under a branch: a
consumer count, a gate's component count and a suite's size all change with no
code changing, and the reader of a bare number cannot tell which tree it came
from. Where the raw output matters, put it in an evidence/ file beside the
task and name that file in the Command column.
| What | Value | Command | Commit |
|---|---|---|---|
6. Notes
Decided with the operator on 2026-10-03: every configuration document is per tenant and per party; currencies, currency pairs and calendar adjustments stay per tenant for now. The policies come from model flags wherever codegen can emit them, so the order is:
- The run document and reporting registry (seven tables, already flag-generated RLS).
- The analytics documents, by flag; the pricing model tables' hand-written policies move to flags.
- Generate the hand-written RLS policies from model flags, so components created before iam get generated policies too.
- Conventions and curve configuration, by flag; text keys gain the party.
Step 1, decided while building it:
- A party-scoped table models
party_id uuid not nulland sets:rls_party_isolation: truebeside:rls_tenant_isolation:, asvariability.system_settingdoes. - The mappers stay party-unaware.
ores::ore::domain::assign_partystamps the party on every row of a mapped document, walking vectors, optionals and structs by reflection; whoever persists a document calls it. - The eventing test template stamped the session party only on the row under
test, so a party-isolated parent it seeded was invisible to the child's
insert trigger. Codegen now marks a party-isolated parent
(
parent_party_isolated) and the template stamps the session party on it. tests/party_fixture.hppmakes two operational parties of the test tenant, with a context that sees only each, for the per-group isolation tests.
Step 2, decided while building it:
- Sixteen analytics tables: pricing engines (three), today's market (five), stress test (three) and credit simulation (five). The collection kind lookup is system-tenant reference data and stays as it is.
- The pricing model tables had hand-written tenant policies with no reason
behind them, since analytics is created after iam. They now set
:rls_tenant_isolation:and:rls_party_isolation:, and the hand-written blocks are deleted. pricing_model_configandtodays_market_configwere unique by name per tenant;party_idjoins their natural key, so the name is unique per party.- The eventing test template also seeds ancestors two or more steps up through
_plan_required_seeds; a party-isolated ancestor there now carries the session party too. - The analytics core tests link
ores.refdata.core, which the generated tests need to create their party, as the reporting tests already did.
Step 4a, the curve configuration, decided while building it:
- The 34 curve configuration tables are keyed by uuid, so
party_idis a plain column with:rls_party_isolation:; they already had the tenant flag from Generate the hand-written RLS policies from model flags. - The party comes from the session, as the operator pointed out: the request
context is built from the JWT (tenant, party, visible parties); every
generated service runs
ores::service::messaging::stamp, which overwritestenant_idandparty_idfrom that context; and the context's session settings are what the RLS policies read. A party-scoped table needs no more than the column and the policy to take part.assign_partyserves only code that writes through repositories directly, which today is the tests.
Step 4b, the conventions, decided with the operator: a convention is keyed by
its ORE id, and two parties must each be able to hold the same id, so
party_id joins the key. Codegen derives the SQL key and the API key from the
same columns, and the party comes from the JWT, so the column is marked to join
the SQL primary key, unique indexes and exclusion constraint while staying out
of the repository and protocol key parameters.
Step 4b, which conventions, decided with the operator on 2026-10-04: the data
splits into world data, which every party sees the same, and party
configuration, which each party owns (World data versus party configuration).
The IBOR index, overnight index and currency pair conventions are world data:
the seed takes them from QuantLib, synthetic feeds need them, and the
publisher writes them. They stay per tenant with no party_id, and move with
the synthetic data rewrite. The other 22 conventions are instrument and
curve-building choices, so they are per party. Tenor conventions were never in
scope; they are a reference lookup.
7. Test Scenarios
Manual QA scenarios (scaffolded via compass add test_scenario)
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 |
|---|---|---|
8. PRs
9. Review
| Comment summary | File | Decision | Notes |
|---|---|---|---|
10. Result
Every configuration document is per tenant and per party. A party reads and
writes only its own rows, and the party comes from the session, set by stamp
from the JWT.
- Reporting: the run document and reporting registry, seven tables (#2579).
- Analytics: pricing engines, today's market, stress test and credit simulation, sixteen tables; the pricing model tables moved from hand-written policies to flags (#2580).
- RLS generation: the session functions run before any table, so every component takes the generated policies; 161 hand-written policy blocks became model flags, and a catalogue diff proved the policies the same.
- Curve configuration: 34 tables (#2584).
- Conventions: the 22 instrument conventions.
party_idjoins the SQL key through the new:sql_key:column property, so two parties can each hold the same convention ids; the repository and protocol keep the id alone. - The IBOR index, overnight index and currency pair conventions stay per tenant as world data, with currencies, currency pairs and calendar adjustments (World data versus party configuration).
Each group has a test that one party cannot see another's rows, and
conventions_party_database_tests.cpp imports the Example_62 deposits under
two parties.