User Journey: Sign off a pricing engine change

Table of Contents

This page documents a user journey: Each approval part that model risk names reads a proposed change to an official pricing engine configuration, approves or refuses it, and a proposal that every part approved is made official. The person who proposed a change cannot sign it off.

A user journey describes what one person is trying to do, before anything is built. It is a design document that outlives any sprint, so it names the screens, the entity fields each screen shows, and the operations and messages each step needs.

1. Goal

"I must decide if this change to the way we price is safe, and I must leave a record that I decided." The model settings of a party are under model risk control. A party holds few official pricing configurations, one for each cycle, and every official report of that cycle runs with one of them. A change to an official configuration therefore changes the numbers of every report that binds it. Each approval part that model risk names must read the change, and the system must keep who approved it and when. The journey is done when the proposal is in force, or refused, or withdrawn, and the record says which and who decided.

2. The approval is the inbox's

The platform already has an approval engine in ores.inbox, and this journey uses it. It does not invent a second one.

  • A proposal is an approval request (ores.inbox.approval_request) of a kind (ores.inbox.approval_kind). The kind here would be analytics.pricing_engines_change. The component that owns the kind owns the detail row, which holds the proposed version.
  • A part (ores.inbox.approval_part) is a function whose approval a request needs. A part is not a role. It names a permission, such as inbox::approvals:decide_market_risk, and a tenant grants that permission to whichever role does the work. Each part has an answer order. A part with a lower order approves first. Parts with the same order answer together.
  • A decision (ores.inbox.approval_decision) is one row for each act: approve, refuse, hold, resume, withdraw, reverse. A decision is never edited.
  • A refusal from a part that can answer ends the request. The requester alone may withdraw it.
  • The schema enforces four eyes. A trigger refuses a decision from the person who raised the request, and a unique index lets one person approve a request once.

Four parts exist today: controller, finance, market_risk and operations. This journey needs three. Market Risk is the seeded part. Quant and Model Validation are new, and their names are assumed until model risk confirms them. The prototype draws Quant first, then Market Risk and Model Validation together. That order is also an assumption.

3. Actor and trigger

Four people take part. All act inside their own tenant, for their own party.

Actor What they do
The proposer, usually a market-risk owner Raises the request, answers a refusal, rebases an out-of-date proposal, withdraws it. Cannot decide it, even when the proposer holds one of the parts.
A signer, one for each part Holds the part's permission. Reads the proposal and approves it or refuses it for that part.
Whoever makes a proposal official Any person who may write the configuration. They do it after every part approved.
A viewer Reads the queue and the proposals. Changes nothing.

The trigger is a proposal. It comes from Change how a product is priced, from Bring in a pricing engine configuration file, from a proposed revert on the History tab, or from a candidate in Author a pricing engine configuration. A signer starts from the queue under Workflows on See the pricing engine configurations a party holds. The journey ends on the proposal's page, with the proposal in a final state.

4. Steps

  1. Open the queue. The person opens Sign-off. The screen is a record list as the Record screen standard draws one. A row is one proposal: the configuration, the cycle it serves, the change as version N to N+1, who proposed it, when, the parts that approved so far, its state and what it needs from the reader. A tag says Needs your sign-off when the reader holds a part that can answer now and did not propose the change. A search runs over the configuration and the change. A filter shows the open proposals, the ones that need the reader, or all, with the closed ones.
  2. Open a proposal. The title names the configuration and the proposed version. The lead line states the state and that the version in force stays in force until the proposal is approved. The tabs are Sign-off, Changes, Reach and History.
  3. Read the changes. The Changes tab compares the proposal with the version it was written on, as a diff, and shows the proposer's reason and commentary. It lists the evidence the proposer attached, such as a back-test.
  4. Read the reach. The Reach tab states the rule: a report needs a price for every product type in its portfolio. It lists the reports the configuration serves, with each report's cycle, portfolio, the products it holds that change, and whether the proposal still prices every product type the report needs. It states that a run made after the change is in force is not comparable with an earlier run for the changed products.
  5. Read the sign-off. The Sign-off tab lists each approval part, its decision, the person who decided, when, and the comment. A part that must wait for an earlier part says so: Waits for Quant. It states the four-eyes rule: the person who raised the request cannot decide it, even when they hold one of the parts, and one person approves a request once.
  6. Approve. A signer who holds a part that can answer now sees Approve and Refuse on that part's row. Approve opens a dialog that asks the signer to confirm that they read the changes and the reports they reach, and takes an optional comment. After the last part approves, the proposal moves to Approved, not in force.
  7. Refuse the proposer. When the signer proposed the change, the row shows no button and says: you proposed this change, another person who holds the part must sign it off. The inbox refuses the same decision already.
  8. Refuse. Refuse opens a dialog that needs a reason. The request ends and the proposal moves to Refused. The version in force stays in force. The proposer reads the reason and changes the proposal and submits it again, or withdraws it. A refusal never deletes the proposal.
  9. Make it official. When every part approved, any person who may write the configuration sees Make version N+1 official. The dialog names the reports it reaches and states that no report is rebound and that the previous version stays in the history. After it, the version is in force, and the next run of each report that binds the configuration uses it.
  10. Rebase an out-of-date proposal. When another proposal became the version in force first, this proposal sits on an old version. The page says so and offers the proposer Rebase. A rebase moves the proposal onto the version in force and clears the approvals, because the parts approved a different base.
  11. Withdraw. The proposer may withdraw an open proposal. It changes nothing and stays in the history.
  12. Read the history. The History tab lists what happened to the proposal, newest first: who proposed it, each approval and refusal with its comment, and when it was made official.

5. Screens and wireframes

Two screens: the queue, and the proposal's page with four tabs. The sign-off has its own screen because it is the control: a part reads one change and decides, and the screen keeps the decision.

wireframe_sign_off_a_pricing_engine_change.png

Figure 1: The sign-off queue and one proposal's page: the approval parts, the four-eyes rule, and the actions.

6. Prototype

The sign off a pricing engine change prototype owns this journey, and its design note says what it argues, which states it can reach and what is fake. It belongs at doc/prototypes/reporting/sign-off-a-pricing-engine-change/.

It must draw the queue and the proposal page as the Record screen standard draws them. It must switch the state of the first proposal behind ?proposal=pending|partly|ready|rejected|superseded and the person behind ?actor=proposer|quant|risk|validator|viewer. The proposer must hold the Market Risk part, so that the four-eyes refusal is reachable. It must show the answer order: Quant answers first, and the other two rows say Waits for Quant. It must show an approval, a refusal with a reason, the point where every part has approved, the making of a proposal official, a rebase and a withdrawal. It must state that the kind, the Quant part and the Model Validation part do not exist, and that their names and order are assumed. Every value is a fixture.

7. Entities composed

The first three rows exist in the analytics and reporting services. The next four exist in the inbox. The last two are new.

Entity What it contributes Model
pricing_model_config The configuration, and the version in force ores.analytics.pricing_model_config
pricing_model_product The rows that change ores.analytics.pricing_model_product
report_configuration The bindings that give the reach ores.reporting.report_configuration
approval_request The proposal: who raised it, when, why, its state ores.inbox.approval_request
approval_decision One part's approval or refusal, with the person, the time and the comment ores.inbox.approval_decision
approval_part The parts, their permission and their answer order ores.inbox.approval_part
approval_kind What the request asks for, and how many approvals close it ores.inbox.approval_kind
a pricing engine configuration change detail The proposed version, held apart from the version in force, with its base version (missing)
a policy row for a put of pricing_model_config Says which parts must approve a write, so that a direct write to an approved version is refused (missing; the inbox holds the table)

8. Operations and messages

The inbox operations are generic. The queue, the decision and the withdrawal need no new subject. The kind, two parts, a policy row and the detail are the new work.

Step Operation Subject Status
1, the queue List the approvals that wait for the reader inbox.v1.ops.list_approval_queue exists; the kind and its detail are missing
1, the reader's own List the requests the reader raised inbox.v1.ops.list_my_approval_requests exists
2, raise the proposal Raise an approval request with its parts inbox.v1.ops.raise_approval exists; the kind is missing
3, the changes Read the proposal and the version it sits on analytics.v1.pricing_engines_documents.get for the version; none for the proposed one partial
4, the reach Read the definitions bound to the configuration reporting.v1.report_configurations.list partial; no read by configuration id
4, the portfolios Read the product types of a report's portfolio (no operation) missing
5, the parts Read the parts, their order and their permission the approval_part read exists as data; Quant and Model Validation are missing
6 to 8, decide Approve or refuse for a part inbox.v1.ops.decide_approval exists; four eyes enforced in the schema
9, make official Make a proposal the version in force (no operation) missing; the apply workflow of the kind may do it
10, rebase Move a proposal onto the version in force (no operation) missing
11, withdraw Close an open proposal inbox.v1.ops.withdraw_approval exists
12, the history Read the events of a proposal the request's versions and decisions exists as data
The screen Sign off from the browser (no BFF route) missing

9. What is missing

  1. The kind and its detail. No approval kind names a pricing engine configuration change, and no detail row holds the proposed version. A proposal must not replace the rows that a run reads, so the detail holds it apart.
  2. Two parts. market_risk exists. A quant part and a model_validation part do not, and neither do their decide permissions. Model risk must confirm the names, the answer order and which role holds each permission. The kind says how many approvals close a request, and one person approves once.
  3. The policy row. The inbox policy says which part must approve a put of an entity. A row for pricing_model_config makes a direct write to an approved version go through a request. Without it, a person who holds a part can write the configuration directly, and the sign-off controls nothing. The service must also read the policy.
  4. Make it official. An approved request applies through the kind's apply workflow, inside an apply window. The prototype draws a person who makes the proposal official instead. The choice is open.
  5. The version a run used. No operation says which version of a configuration a run was made with. The sign-off proves that a version was approved, and the run cannot prove it was made with that version. Model risk needs both.
  6. A report binds a configuration, not a version. report_configuration holds the configuration. Making a version official must change what the next run reads, in one step, with no rebinding.
  7. A candidate that replaces the official configuration. A candidate from Author a pricing engine configuration also moves the reports of a cycle from one configuration to another. That is a rebinding step, and the prototype does not draw it.
  8. Rebase. The inbox has no operation that moves a request onto a newer base.
  9. The evidence. A proposal carries a back-test or a note. No operation attaches a document, and the prototype draws the files as a fixture.
  10. Hold, resume, expiry and reversal. The inbox has these decisions and an expiry. The prototype draws approve, refuse and withdraw only.

10. Related journeys

11. Related knowledge

12. See also

Emacs 29.3 (Org mode 9.6.15)