User Journey: Author a convention for a kind
Table of Contents
This page documents a user journey: A person writes a convention that no curated document states, for one kind and one party.
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
"We trade something the convention list does not cover, and the run has to read a term set that states it. I want to write that set down once, for my party, and be able to show later what it said and who changed it."
The journey is done when the new document exists, its terms are the ones the person meant, and its record shows the version, the author, the time and the reason. That last part is not paperwork: the terms are copied into a conventions file that a run reads, so the record of who wrote them is the record of what a run read.
This is a different job from Change the convention a party uses for one kind. There the document already exists and the person picks it or corrects it. Here nothing exists — the person is the author of a term set, and the curated list is one more thing that was authored before them.
2. Actor and trigger
The actor is the person who owns the market terms the desks book on: a market
risk owner acting inside their own tenant, a tenant administrator, or an
account holding Operations, which holds refdata::<kind>_conventions:write
for every kind. A market risk owner knows the terms and asks for them; an
operations account holds the write and makes it.
The trigger is a trade whose terms no document states, or a term set the party agreed with a counterparty that the curated dataset does not carry. The person arrives from the kind's list — having found that the document they need is not there — or from a trade whose booking is blocked on it. The journey starts on one kind and ends when the new document is saved and its history reads back.
This journey writes one new convention row of one kind. It does not write a report definition, a binding, a schedule or a trade, and it does not run anything.
The journey keeps no server state. No step waits on the server after its reply.
3. Steps
- Establish that the terms are not already there. The person opens the kind
and searches for the terms they need. The generated filter takes
id_one_ofand nothing else, so the search is a page of rows read in words, and on a kind of 320 documents it is a real cost. The step exists because authoring a document that duplicates one of the 201 existing term sets is the mistake this journey must not make, and the screen has no cheap way to prevent it. See What is missing. - Choose the kind. The person picks the convention kind that serves the
instrument family they are booking. There are 26 and each is its own table
with its own columns, so the kind is chosen before any field is drawn: the
screen draws the fields the chosen kind's table carries and no others. A
swaprow states a fixed calendar, a fixed frequency, a fixed convention, a fixed day count, an index, a float frequency and a sub-periods coupon type; adepositrow states whether it is index-based and, if not, its own calendar, convention, day count and settlement days. - Read the fields, and what each one is. The system shows every term column the kind carries, grouped by the part of the schedule it belongs to, and says of each whether it is a pick from another reference list or a value that lives only on this row. A pick holds the list's code and the screen shows the code and the label. The screen states where the grouping comes from: it is the screen's reading, because the table carries no leg or schedule object.
- Author the terms. The person fills the fields. Every field the model marks as required is required; every nullable field may be left empty, and the screen shows it as empty rather than defaulting it, because empty and stated are different claims a later reader has to tell apart. A term that is a pick is not created here: the screen links to the list that owns it, and says so.
- Review. The system shows the term set in the table's own column order, a count of the fields the person filled, and, for each optional column left empty, that it is empty. Nothing is written until the person confirms.
- Save. The person saves. The system writes the row with one request,
refdata.v1.<kind>_conventions.put, carrying the row, amust_not_existprecondition and achange_intent. The precondition is what makes this safe: a key already present is refused rather than overwriting a document someone else wrote. The intent carries a reason code fromdq.v1.change_reasons.listand the person's own commentary. The service stampsmodified_by,performed_by,valid_fromandvalid_tofrom the session, and the row'sversionstarts at 1. - See the result. The system reports the document's key, its version, and what the write did not do: it did not mark the document as one a run takes, because no such mark exists, and it did not attach the document to any report, because a conventions configuration holds no rows to attach it to. What the person has actually done is add a document that every run of the party will now carry.
- Read the record. The person opens the history. The first version shows the
terms as authored, the author, the time and the reason. The bitemporal
envelope is what this step is about: a later correction closes this row with a
valid_toand writes version 2 with avalid_from, so this version stays readable, and the two can be compared field by field. - Leave, or go on. The person returns to the kind's list, or goes to Change the convention a party uses for one kind to correct what they just wrote, or to See the conventions a party uses to read the kind back.
4. Screens and wireframes
One screen: the kind in the header, the fields the kind's table carries grouped by the part of the schedule, the review panel, the outcome, and the history as the outcome step's own view. The rail shows the steps in order, and the last step cannot be returned past.
The screen is a form over one table, because that is what authoring a convention is. It draws the fields it reads from the table and invents none: the 26 tables have different columns and a screen that drew a single shared form would be claiming a shape the data does not have.
Figure 1: Author a convention for a kind: the kind chosen, the fields its own table carries with pick and free-text kept apart, the review, the saved document with its bitemporal record, and the version history.
5. Prototype
No prototype is written for this journey in this pass. The act it describes is a form over one table and there is no open interaction question in it, which is the condition for spending a prototype; the questions that are open here are about the server and are recorded in What is missing.
Conventions are named convention sets: a set carries a name, a purpose and a description, a system holds few of them, and a report binds one by choosing it. Authoring a set is its own journey, Name a convention set, and its prototype is Name a convention set. Nothing here draws it. The decision, and the gaps that block it, are recorded in the story Name the convention sets a report chooses (DE6C0BDA-D12A-4E31-AA86-C323F5A7E016). The report side of the choice is drawn in Create a report and Change what a report runs, and those two pages are the choosing surface this journey feeds.
6. Entities composed
| Entity | What it contributes | Model |
|---|---|---|
| The kind's convention table | The row this journey writes: every term column of the chosen kind, plus version, valid_from, valid_to, modified_by, performed_by, change_reason_code and change_commentary |
one of the 26; see below |
change_reason |
The reason code the person supplies, from the tenant's own list | |
calendar_name, day_count_fraction_type, business_day_convention_type, payment_frequency, floating_index_type, sub_period_coupon_type |
The lists a pick is a code from |
| Entity | What it contributes | Model |
|---|---|---|
swap_convention |
The worked example: seven term columns, of which five are picks and two are required text | ores.refdata.swap_convention |
deposit_convention |
The contrast: index_based decides whether the row states its own terms or inherits them, so the same kind draws two shapes |
ores.refdata.deposit_convention |
ibor_index_convention |
The tenant kind: a document authored here belongs to no party, so it is authored once for the tenant | ores.refdata.ibor_index_convention |
overnight_index_convention |
The same, for the overnight index | ores.refdata.overnight_index_convention |
tenor_convention |
The one kind with a description column, and the one kind keyed by code |
ores.refdata.tenor_convention |
currency_pair_convention |
The tenant kind keyed by pair_code, whose calendars are a child table |
ores.refdata.currency_pair_convention |
party |
The owner of a document authored on one of the 22 party kinds | ores.refdata.party |
The writable fields are the table's own columns and nothing else. The row's key
is id on 24 kinds, code on tenor and pair_code on currency_pair, and
the person supplies it: nothing generates a key, and the key is what a
conventions file refers to a document by.
The envelope is the table's, not a separate store. version starts at 1,
valid_from is the write's time, valid_to is the store's infinity until the
next version closes it, modified_by and performed_by come from the session,
and change_reason_code with change_commentary come from the request. The
convention tables are the only record of who wrote a term and why.
7. Operations and messages
| Step | Operation | Subject | Status |
|---|---|---|---|
| Establish that it is not there | List the kind's documents | refdata.v1.<kind>_conventions.list |
exists |
| Establish that it is not there | Read one document | refdata.v1.<kind>_conventions.get |
exists |
| Author the terms | Fill the calendar picker | refdata.v1.calendar_names.list |
exists |
| Author the terms | Fill the day count picker | refdata.v1.day_count_fraction_types.list |
exists |
| Author the terms | Fill the business-day convention picker | refdata.v1.business_day_convention_types.list |
exists |
| Author the terms | Fill the payment frequency picker | refdata.v1.payment_frequencies.list |
exists |
| Author the terms | Fill the floating index picker | refdata.v1.floating_index_types.list |
exists |
| Save | Offer the change reasons | dq.v1.change_reasons.list |
exists |
| Save | Write the new document | refdata.v1.<kind>_conventions.put (must_not_exist) |
exists |
| Read the record | List the document's versions | refdata.v1.<kind>_conventions_versions.list |
exists |
| Read the record | Read one version | refdata.v1.<kind>_conventions_versions.get |
exists |
| Read the record | Read every version with its diff | refdata.v1.history.get (entity_type = ores.refdata.<kind>_convention) |
exists |
| See the result | Announce the write | refdata.v1.<kind>_conventions_events.created |
exists |
| Establish that it is not there | Search the kind by a term | (the filter is id_one_of and nothing else) |
missing |
| Attach the document to a report | Record which document a run takes | (no default column, no default row) | missing |
| The screen itself | Author a convention from the browser | PUT /api/conventions/<family>, open for the deposit and swap families only |
partial |
The per-kind subjects are the generated ones: refdata.v1.swap_conventions.put,
refdata.v1.tenor_conventions.put, refdata.v1.currency_pair_conventions.put
and their equals. swap_conventions_versions.list and .get read the versions
of one document; refdata.v1.history.get reads them with a diff.
PUT /api/conventions/<family> exists. It takes a version and a change intent,
sends the put with must_not_exist when the version is null, and returns the
written document. The BFF opens it for the deposit and swap families only, and
refuses the other families with "not drawn yet". This journey opens the other
kinds and validates the person's terms against the term lists before the write.
The route carries no party id in its path.
8. What is missing
- The new document is indistinguishable from 320 others. A row is authored
with a key the person chose and no name or description, so nothing a reader
sees says what it is for. The only kind with a description column is
tenor. The screen derives a name from the terms, and a derived name collides: the party already holds 201 distinct term sets across 320 swap rows. The person who authors the 202nd has authored a document a reader cannot pick out by name. - Nothing prevents authoring a duplicate of a document already there. The
precondition is
must_not_existon the key, so a new key with terms identical to an existing row is accepted. Finding the duplicate first needs a term search the list does not offer, so the check is a person reading 320 rows. - The new document is not marked as the one a run takes. Authoring adds a row to the party's conventions document, and the export writes every row of that document. So the run reads the new document and reads the 320 old ones equally and has no way to be told which applies. A default marker is the missing piece, exactly as it is for Change the convention a party uses for one kind.
- A refused write carries no field-level record. A
must_not_existviolation or a term naming a value the list no longer holds arrives as a plain error with a code and a message, and the screen has to name the offending field itself. - No convention field validates its term. The models declare no foreign key and no lookup check on a convention's term columns, so the server stores any text and a term list that no longer holds a code does not refuse the write.
- The history has no draft version.
refdata.v1.history.getreturns saved versions only, so the history view is a post-write view and a person cannot compare what they are about to write against what is stored. - An authored document does not travel. There is no route from a tenant's authored row back into the curated DQ dataset, so a term a party needs is authored again in every tenant, and the next publish cannot carry it. The DQ tables are seeded in the system tenant and published downward; nothing writes a tenant's row upward.
- A tenant-scoped kind is authored for the whole tenant. Authoring a
currency_pair,ibor_index,overnight_indexortenordocument writes a row no party owns, so it is one document for every party of the tenant. The screen states the scope; nothing refuses the write on that ground. - No four-eyes control is seeded. A new term set that every desk's runs will read cannot be second-checked: the approval operations exist, no request kind is seeded for a convention, and nothing applies a change when the last decider approves.
9. Related journeys
- See the conventions a party uses — the read that shows the new document exists and what its terms say
- Change the convention a party uses for one kind — the neighbouring job, when a document already states the terms
- Publish conventions from data quality — how the curated documents arrive, and why an authored one does not travel back
- Maintain a classification list — a term the person needs may be missing from a list rather than from a convention
- Add or correct a currency — a cross-currency or an FX convention names the currencies whose pair it serves
- Set up a currency pair — the pair and its convention are the two faces of one record
- Tune the tenant — the change reasons a convention write carries are tenant data
- Define how tenors resolve — the tenor convention a new tenor document resolves under
10. Related knowledge
- Conventions journeys — the group this journey belongs to
- Reference Data — versioning, provenance, change reasons and the precondition
- UX Language — the badge and form vocabulary the terms panel follows
- ORE Model Configuration — the ORE
conventions.xmlelements an authored row exports to - Date Rolling and Business-Day Calendars — the calendars and business-day rules a term names
- Term Structures and Tenors — how a tenor resolves
- Journey execution — how the screen composes its reads
- User Journeys — the index
11. See also
- User Journey Extraction Standard — the checklist this journey is written against
- Document type: user_journey — the eleven sections this page fills
refdata.v1.<kind>_conventions.put— the one write, withmust_not_existormust_match_version