Task: Expose the configuration document operations from each owning service
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 service saves, reads and deletes another component's configuration documents only through that component's operations.
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-04 |
3. Acceptance
- Each owner has an operation model and a handler for save, get and delete of its documents, under the caller's session and with seeded permissions.
- Each handler is tested over NATS against a running service.
- Every owner tests save, find by configuration and get of its own documents through its operations, so analytics and refdata no longer rely on the ORE corpus round trips for their coverage (raised in the review of PR #2624).
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
Part of the redesign the operator asked for on 2026-10-04, after ruling that a service reading or writing another component's tables is an architectural error (No service reads or writes another component's tables). Order: this store move, the owners' operations, the import saga, then the package step.
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
| PR | Title |
|---|---|
| #2639 | [refdata,analytics,reporting] Serve configuration documents over NATS |
9. Review
| # | Comment summary | File | Decision | Notes |
|---|---|---|---|---|
| 1 | Reporting writes as the owner for a party-less session, which a user token without a party could reach | run_document_handler.cpp | Accept | Every write requires a session that acts for a party (require_party); a party-less session may only read, as the owner. A test proves a party-less delete is refused and a party-less get succeeds. |
| 2 | Refdata and analytics save and delete have no party guard | configuration_document_handler.cpp | Accept | The same rule on every write; a test proves a party-less save is refused. |
| 3 | remove is not atomic; deleting a missing document succeeds | document services | Accept, in part | Each remove and the protocol say so; the delete is idempotent for compensation, with a test. Atomicity stays with Import a run's configuration in one transaction. |
| 4 | Save returns the id the client sent | configuration_document_handler.cpp | Decline | The store writes the given id; only the party is stamped. |
| 5 | for_requested_party reads every party on a bad id | handler_helpers.hpp | Accept | A non-empty id that does not parse is refused, with a test. |
| 6 | authorise is copied into three handlers | handlers | Accept | One authorise in ores.service/messaging/authorise.hpp. |
| 7 | A dangling configuration_id is no longer caught | models | Decline | The saga creates the configuration through reporting before it saves the document, so the id is real by construction. |
| 8 | Missing tests | tests | Accept, in part | Added party-less writes refused, a missing delete and a bad party id; the cross-party refusal already exists; a partial-failure remove is not tested, since remove is not atomic by design. |
10. Result
Refdata, analytics and reporting now serve their configuration documents over NATS, so the ORE service can import and export a run without touching their tables.
- Operation models, with the TypeScript facet off since no UI reads them:
ores.refdata.configuration_document_messages(curve configuration save, get and delete; conventions save and get),ores.analytics.configuration_document_messages(pricing engines and today's market save, get and delete) andores.reporting.run_document_messages(run document save, bind, get and delete). - Handlers and registrars in each owner's core, under the caller's session. A
get may name a party when the session acts for none, as a workflow step's
does (
for_requested_partyinores.service). Reporting acts only on a definition the session's party owns, or as the owner for a session with no party. - Delete on every document service, for the saga's compensation.
refdata::conventions:readand:writeare seeded; the other operations use their header table's existing codes.- Five headers (curve configuration, pricing model config, today's market
config, stress test library, credit simulation config) checked
configuration_idagainst reporting's table in their insert trigger, which runs with the inserting service's grants and so would have failed under the owners' own grants, and broke the rule besides. Their foreign keys now carry:skip_check:, which drops the trigger check and keeps the generated list by configuration operations; reporting's binding holds the reference. - Tests drive each handler over real NATS with a minted token
(
ores.testing/handler_test_support.hpp): save, get by configuration and delete per owner, the permission refusal, and reporting's cross-party refusal. ctest 90/90 after a database recreate.