Task: Build the First run journey

Table of Contents

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

1. Goal

An empty installation leads one person from a welcome screen to a working tenant on one rail: the system administrator is created, the first tenant is described and provisioned, its administrator takes over, signs in for the first time and reaches Ready. The steps that create a tenant are the same steps the New tenant journey runs, written once and placed on both rails.

2. Status

Field Value
State DONE
Parent story Implement the setup journeys in ores.web
Now Nothing.
Waiting on Nothing.
Next Nothing.
Last touched 2026-09-29

3. Acceptance

  • An empty installation opens on the journey: a welcome screen, then one flat rail that carries the new tenant steps inline, the hand-over, the administrator's first sign-in and Ready.
  • Creating the administrator closes bootstrap mode, and the journey signs in as that account, so the starting-point read and the provisioning request are made by the account that owns them.
  • The starting-point cards and the tenant form are the server's seed profiles: each card states the profile's own counts and bullets, and the form is built from the parameters the profile declares.
  • The provisioning step renders one line per workflow step from the progress read, asks again while the run is open, and retries from the failed step without re-running the steps that completed.
  • A first sign-in whose account must change its password sets one against the rules the server's policy read answers with, and chooses the party to start in when the account works in more than one.
  • The person reaches Ready, and the journey states what to do next.

4. Plan

First run is nine steps on one rail, and the middle five are the New tenant journey's steps. So the unit has two halves: the step library every tenant journey shares, and the three steps that belong to the first run alone.

The journey is a mode, not a page. While the installation is in bootstrap mode the route table shows the journey on every path, which is the rule it already follows for the setup form. Creating the administrator clears that mode, and the journey continues past it, so the route table carries a second reason to stay: the journey has begun in this tab. Finishing it hands the browser to the ordinary routes. The position on the rail is memory and not storage, so a reload leaves the journey at the sign-in screen — the behaviour Journey execution already records.

The new tenant steps are a library, and first run inlines them. The step list is data: one definition per step, and the rail is derived from the same list, so a step cannot appear on the rail and not in the page. First run concatenates its own steps with the shared ones rather than nesting a journey inside a journey, and the two rails address the shared steps by name, so a step added to one journey cannot shift what another journey means.

The journey's state is one hook. The chosen starting point, the typed details, the run's id, and the passwords held in memory live in one place, and every step body is a function of it. A password reaches no storage: it is typed, used for the sign-in it belongs to, and dropped when the hand-over ends.

The browser signs in as the administrator it creates. The starting-point read and the provisioning request are made by an account, and until the administrator exists there is none. The journey therefore signs in with the credentials it just created, before it reads a starting point. A tenant administrator signs in as `/username@hostname/`, which the profile prefilled or the person typed, because that is what routes the request to its tenant.

The form is the profile's. The cards are the server's rows, each showing the bullets, the counts and the settings that row declares, and the tenant form is built from the parameters the profile carries. No profile's name or shape is written in the browser, so a third profile is a row and a new setting is a row and a column.

Progress is read, not pushed. The provisioning step asks the progress read again while the run is open, renders one line per step the run materialised, and offers the retry when the run stopped. A retry that refuses is an answer to show, not an error to raise: the engine says which step it re-dispatched or why it will not.

The rules the screen shows are the server's. The forced change and every password field read the policy the server answers with, so the copy the field kept until now becomes the one the read fills, and a deployment that tightens a rule tightens the screen without a client release.

Nothing is walked back past a write. The steps that create server state are final, so the rail offers no way backwards into a half-finished write, and a step that fails does not advance.

What this task leaves. The New tenant and New party journeys are the next step's, and the shell's provisioning verbs move to the same contract in the one after that. There is no Tenants page yet, so Ready offers the way home rather than a second tenant. Discarding a half-provisioned tenant stays the administrator's own action, as the story records.

The copy is translated. Every message the journey shows joins the catalogue, which carries all three languages, so the screens are readable in each and the catalogue's own test keeps them at parity.

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
The rail a browser renders on an empty installation nine steps, the welcome screen first, and no sign-in form google-chrome --headless --dump-dom http://127.0.0.1:21402/ 3523c3ec09
The starting points the server answered two profiles: Operational, three steps and two parameters, forcing a change; ACME demo, six steps and no parameters, reusing the creating password GET /api/seed-profiles 3523c3ec09
The steps the ACME demo run completed six of six, and complete_provisioning completed with warnings the progress read, GET /api/provision-tenant/:id 3523c3ec09
The step the Operational run stopped on step 3, import_lei_hierarchy, because the walk named a root LEI the deployment does not hold (a mistyped code; the datasets' small hierarchy holds 668 parties for a correct one) the progress read, GET /api/provision-tenant/:id 3523c3ec09
The forced change a first sign-in reported, and after the change passwordResetRequired: true, then false, and the new password signs in POST /api/account/password 3523c3ec09
The parties the ACME administrator chooses between five, one of them the default the login named POST /api/session 3523c3ec09
The web suite 75 cases in 9 files, all passing npm test in projects/ores.web 3523c3ec09

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
Walk the first run journey in a browser PENDING The whole rail on an empty installation; adds the two steps the unit tests do not cover in a browser, the party choice and the password of the person's own.

8. PRs

PR Title
#2356 [web] Build the first run journey

9. Review

# Comment summary File Decision Notes
1 The welcome screen reads as dense on a laptop, and the splash belongs to the panel rather than to one step's body. FirstRunJourney.tsx, PublicShell.tsx, JourneyPage.tsx Fixed The public shell gives a journey its own width rather than a form's; the splash frames the step panel above every step, with the tenant being described beside it once a starting point is chosen; the lead names the rail, so the count in the copy matches the count on the page; and the three stage cards state what each stage leaves behind instead of repeating the step titles.
2 The forced password change is a client flow; confirm the server refuses other calls while the account owes one. FirstSignIn.tsx, auth_handler.hpp Captured It does not. The flag is set by the provisioner and reported by the login answer, and nothing in the request path reads it, so the rule is the screen's alone. Filed as A forced password change is advisory.
3 tenantPrincipal builds username@hostname; confirm the routing accepts a hostname that states a port. state.ts, auth_handler.hpp Fixed It does not: split_principal takes everything after the @ as the hostname and auth_lookup_tenant_by_hostname matches it exactly, so a port produced a principal that resolved to no tenant and the tenant's own administrator could not sign in. check_tenant_hostname refuses a port when the tenant is created, with a unit test.
4 The strength heuristic should not be presented as a password rule. ui/passwordPolicy.ts No change The rules are listed by name from the server's policy and shown as met or unmet; strength is an adjective written under them, not a rule the deployment states.
5 Add a browser walk of the party choice and the forced change before release. — Done Walk the first run journey in a browser, which walks the whole rail and photographs it.
6 The Operational card's run stops on its LEI import on a fresh installation. — Withdrawn The finding was wrong: the walk that found it sent a mistyped LEI. The import reads the hierarchy the deployment holds and completes, and the count it creates was measured against the publication.

10. Result

The first run journey is built and proved on a fresh installation. An empty deployment opens on the welcome and then on one rail of nine steps: the administrator is created, the first tenant's steps run inline, its administrator takes over, signs in for the first time and the screen reads Ready. The evidence beside this task carries the browser's rendering of that rail, the journey's calls in the journey's order, and the state the two runs were left in.

The five tenant steps are a library and not a copy. The first run places them between its own steps, so the New tenant journey runs the same ones on its own rail, and the two rails address a step by name rather than by position.

Three things the journey needs were settled here rather than assumed. It signs in as the administrator it has just created, because the starting-point read and the provisioning request belong to an account, and it holds that password in memory for no longer than the journey, because a profile may hand the password to the tenant's administrator. The browser is held on the rail after bootstrap mode closes, which is why the route table carries the journey as a mode of its own. And every password field is built from the policy the deployment answers with, so the copy the field kept until now is gone.

What the two walks found. The ACME demo card completed every step it orders, its tenant reached active, and its administrator's first sign-in asked for a party because the account works in five. The Operational card reached the forced change — the login answer carried passwordResetRequired: true, the change succeeded, and the new password signed in — but its run stopped on the LEI import, because the deployment holds no GLEIF rows for the root LEI the walk named. The journey behaved as designed there: the rail showed the failed step, the retry was offered, and the tenant stayed in bootstrapping. That reading was wrong, and the walk's input was: the LEI was mistyped. Publishing the same dataset for the same tenant with a correct root LEI inserts 668 parties, so the import reads what the deployment holds and the card works. What the walk did find is real and still open: the form takes any string, and a code the deployment does not hold arrives as the publication's SQL rather than as a sentence about the entity. The capture that recorded the data gap has been withdrawn.

What this task leaves: the New tenant and New party journeys are the next step's, the shell moves to the same contract in the one after that, and the proof of all three journeys on a fresh installation is the last.

The journey's first screen was reviewed on the running deployment after the walk. The panel was reworked rather than the words alone: the public shell had held every public screen to a form's width, which left a rail and a step about four hundred pixels and wrapped every line of both. A journey now asks for the wider container. The splash became the panel's header, so it frames every step rather than one, and the tenant being described stands beside it once a starting point is chosen. The lead names the three things the setup creates, in the order the cards beside it state them, so nothing on the screen claims a count the rail does not show, and the cards say what each stage leaves behind. The copy is in all three catalogue languages.

Emacs 29.3 (Org mode 9.6.15)