Task: Build the administrator's Rescue access screen

Table of Contents

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

1. Goal

Run Rescue access in ores.web against the real server, on variant B of the accepted prototype: the account's security state leads and names the action it suggests, and the recovery action and the lock control sit under it.

The administrator finds the colleague, reads the account and its login record, and locks or unlocks the account through two BFF routes this task adds. The recovery link is drawn as the primary action and is unavailable, with the reason on the screen, because nothing in the tree sends mail, no subject requests a reset and no table holds a token.

2. Status

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

3. Acceptance

  • The screen is reachable from the signed-in shell and reads the account list, one account and that account's login record through the BFF routes that already serve them.
  • Lock and unlock are one control that shows the state it produces, and each one writes through its own BFF route and its own subject (iam.v1.accounts.lock, iam.v1.accounts.unlock), each guarded by the permission the catalogue grants.
  • The screen states what a lock does not do: it leaves open sessions open.
  • Send the recovery link is drawn as the primary action and is unavailable, and the screen says why: no mail path, no token store and no subject.
  • Every panel whose data has no read path says the data is not available.
  • The rejected variants and the design decisions stay on the prototype task; the build carries variant B and no other.

4. Plan

  1. Add the two write routes the journey needs — POST /api/accounts/:username/lock and POST /api/accounts/:username/unlock — with their wire schemas, their BFF handler tests and their client methods.
  2. Build /rescue from the accepted variant B, composing AppShell with the shared primitives and the account, login-record and session reads the group already landed.
  3. Answer the fallback question the prototype raised, and record the answer.
  4. Drive the screen against the running fleet, then land it.

5. Notes

The one question this task must answer first. The accepted prototype keeps an administrator-typed password as a collapsed fallback for a mailbox that cannot receive, and asks whether it survives the build. It does not. The product owner settled the direction when they replaced the reset form with the emailed link, and a control that has to disclaim its own record — an administrator who types a password knows that password — is worse than no control. The iam.v1.accounts.reset-password subject stays without a browser caller, the screen draws no password field, and Rescue access is corrected in this branch to match. The link is the whole mechanism.

The recovery link is not this task. It needs a token table, a request subject, a completion subject and a mail path, and it is filed as The tree has no way to send an email, and password recovery needs one. This task draws the action and states the gap, which is what the story's rule asks of a control the server cannot perform.

The role the screen assumes. iam::accounts:lock, iam::accounts:unlock and iam::accounts:reset_password are the administrator's; iam::accounts:read and iam::login_info:read are what the reads need. The inventory records where the catalogue grants them, and a role that holds the writes without the reads would see the screen refuse its own panels.

6. 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
     

7. PRs

PR Title
#2493 [web] Build the administrator's Rescue access screen

8. Review

Comment summary File Decision Notes
       

9. Result

Built 2026-10-01.

Rescue access is a route behind the signed-in shell. It reads the tenant's accounts, one account and that account's login record through the routes the group already landed, and it writes through two routes this task adds: POST /api/accounts/:accountId/lock and POST /api/accounts/:accountId/unlock, each forwarding to the subject that already existed. The screen is the accepted prototype's variant B.

The prototype's open question is answered and recorded on the parent story: the administrator-typed password fallback does not ship. The screen draws no password field, iam.v1.accounts.reset-password keeps no browser caller, and Rescue access and its wireframe are corrected to match.

The account list has no page yet, so /rescue with no username picks a colleague out of the tenant's accounts and links to /rescue?username=<username>. The accepted variant does not keep the roster in view, so a control returns to the picker. Every panel the server cannot serve says so: the recovery link is drawn unavailable with its reason, and the lock panel states that a lock leaves open sessions open.

Verification. npm run typecheck passes across the three packages, Prettier is clean, and the suites are green: bff 102 tests in 21 files, web 192 in 18, wire-protocol 51 in 7. Six of the web tests render the loaded screen from a fixture and assert the state it names, the recovery link being unavailable with its reason, the absence of any password field, and the gap list.

The write path was driven end to end against the running fleet on port 21803: a fresh sign-in, iam::accounts:lock through the new route, the login record then reading locked: true, unlock through the new route, the record reading locked: false, and a fresh sign-in answering 200 again. The lock also refused a fresh sign-in with 401 while it was in force, which is what proves the write reached the server's own state rather than the route's copy of it.

The per-account refusal is covered by a route test rather than by the fleet: nothing in the environment holds a second person account, and locking the only one leaves nobody who can unlock it.

What is still missing. The screen has no test that drives the browser, because the package has no DOM harness; the panels are rendered from a fixture instead. And the roles the screen assumes are worth checking before a tenant administrator uses it: the reads need iam::accounts:read and iam::login_info:read, and the writes need iam::accounts:lock and iam::accounts:unlock.

Emacs 29.3 (Org mode 9.6.15)