Task: Paint the tenant status with the badge catalogue

Table of Contents

This page documents a task in the Implement the profile journeys in ores.web story. It captures the goal, current status, acceptance, and any notes or results.

1. Goal

The roster stops deciding what a tenant status looks like. The badge catalogue in ores.dq already maps every tenant_status value to a badge with its own label, tooltip and colours, and the screen uses that instead of a table of its own.

2. Status

Field Value
State DONE
Parent story Implement the profile journeys in ores.web
Now Nothing.
Waiting on Nothing.
Next Nothing.
Last touched 2026-10-01

3. Acceptance

  • The screen paints a tenant's status with the badge the catalogue maps that value to, and holds no colours, labels or tones of its own.
  • The read joins the domain's mappings to the badge catalogue, so a screen asks one question and holds one table.
  • A value the catalogue does not map is written plainly rather than hidden or painted like everything else.
  • A mapping whose badge has left the catalogue is dropped rather than answered with empty colours.
  • A domain nobody has mapped answers an empty catalogue, not a refusal.
  • Every new behaviour is proved by a test, and the pill was read in a browser against the running deployment, colour and label compared to the catalogue.

4. Plan

  1. Read the catalogue, not the colours. The mapping subject already filters by code domain and the catalogue is a few dozen shared rows, so the two reads are one join. The join belongs on the server, because a screen asking how to paint one value should not hold two tables to find out.
  2. Say which domain, not which colour. The screen names the code domain its column belongs to. That is a fact about the column, and it is the only thing the screen decides.
  3. Then the pill. The badge's label, the two colours and the words behind it all come from the answer, and a value with no badge is written as the server sent it.

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
What Value Command Commit
The badge the catalogue maps a tenant's status to active, #22c55e, label Active curl -b cookie http://127.0.0.1:21402/api/badges/tenant_status the branch tip
BFF suite 20 files, 97 tests npm run test in projects/ores.web/packages/bff the branch tip
Web suite 17 files, 184 tests npm run test in projects/ores.web/packages/web the branch tip
Wire contract suite 7 files, 51 tests npm run test in projects/ores.web/packages/wire-protocol the branch tip
Live roster harness 19 promises node evidence/tenant_roster_check.js the branch tip

6. Notes

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
#2478 [web] Paint the tenant status with the badge catalogue

9. Review

Comment summary File Decision Notes
       

10. Result

The roster's status pill is the platform's, not the screen's.

  • The catalogue answers, and the screen asks by domain. A new GET /api/badges/:domain joins the domain's mappings to the badge catalogue and answers what each value looks like. The roster names tenant_status and nothing else; the label, the two colours and the tooltip all come from the answer. The hardcoded tone map and the four tenants.state.* translations it needed are gone.
  • A value with no badge is written plainly. It is a normal state, not an error: a tenant in a state nobody has mapped is exactly the row somebody needs to see, and the screen would hide the interesting case by swallowing it. A mapping whose badge has left the catalogue is dropped for the same reason — a pill with no colours is worse than the value written out.

Proved live: the harness reads the catalogue, then reads the pill the browser drew and compares the two. The pill is painted rgb(34, 197, 94) for the catalogue's #22c55e, carries its label Active, and carries its description behind it. A colour that merely looked right would fail the check.

One thing the review should decide. The catalogue maps suspended to a badge named Frozen and terminated to one named Archived. That is the seed's own vocabulary, and the same mapping serves party_status, whose Suspended value also becomes Frozen: the platform reuses a small badge vocabulary across domains rather than naming a badge after every value. So a suspended tenant will read Frozen. It is faithful to the model and it may be exactly what was intended. If it is not, the fix is a row in the mapping or a badge of its own, not a table on the screen.

What this task does not do. The badge label is server data in English, so a French or Portuguese interface now shows an English status. The answer carries the badge's own code, so a screen can key a translation on it later; nothing does yet. No other screen uses the catalogue, and none is changed.

Emacs 29.3 (Org mode 9.6.15)