Story: Implement the membership journeys in ores.web

Table of Contents

This page documents a story in Sprint 27. It captures the goal, current status, acceptance criteria, and the tasks that compose it.

1. Goal

The two Membership journeys run in ores.web against the real server. The member reads the parties their account works in, sets the party quick sign-in uses, and switches the party the session acts for. The tenant administrator reads the reporting line as a tree and corrects it, with every change recorded and no loop.

2. Status

Field Value
State DONE
Parent sprint Sprint 27
Now Nothing.
Waiting on Nothing.
Next Nothing.
Last touched 2026-10-09

3. Acceptance

  • Every Membership journey in the catalogue runs in ores.web against the real server.
  • A party reads as a name, never as a bare identifier, on both journeys.
  • The server refuses a reporting-line change that would make a loop, and the tree screen states the refusal.
  • Every gap a journey document records is closed or stated on the screen, and the statement is recorded on this story.

4. Tasks

Task State Start End Description
Scaffold story: Implement the membership journeys in ores.web DONE 2026-10-09 2026-10-09 Story scaffolding rides this task: documents, sprint wiring, and the scaffold PR. Close it before merging that PR.
Prototype Where I work DONE 2026-10-09 2026-10-09 [wayfinder:prototype] The member's screen: the identity summary, the parties the account works in, the default party, the switch, and the gap where no client path exists.
Prototype Reporting lines DONE 2026-10-09 2026-10-09 [wayfinder:prototype] The administrator's screen: the reporting tree, the selected account's line, the manager picker with a change reason, and the gap where no approval exists.
Read a member's parties with their names BACKLOG     The member's own party list cannot be named: the association read carries party_id alone and the sign-in reply is the only read that returns a name. Give the member one composed self read of the parties their account works in, each with its name, code and category, plus the stored default.
Let a member set and clear their default party DONE 2026-10-09 2026-10-09 iam.v1.ops.set_my_default_party exists on the server and no BFF route, client method or control reaches it, and the request cannot say 'no default'. Wire the write end to end and make clearing the default expressible.
Refuse a reporting line that would make a loop DONE 2026-10-09 2026-10-09 Nothing refuses A reports to B when B already reports to A. The server accepts the pair and the result is a cycle with no root. Add the check where the write happens and test it.
Give the reporting line its own write DONE 2026-10-09 2026-10-09 Setting one field means sending the whole update_account_request, so a concurrent edit to another field is silently overwritten by the stale copy the screen holds. Add a narrow write that carries the manager and the change reason, with the account version as a precondition.
Read the reporting tree DONE 2026-10-09 2026-10-09 The server answers one flat page of accounts with reports_to_account_id repeated per row, so the screen reads the whole roster and nests it in the browser. Give the tenant administrator a read of the reporting shape: each account's manager and depth, so a large tenant can page a branch.
Build the Where I work screen DONE 2026-10-09 2026-10-09 Build the accepted variant A of doc/prototypes/membership/where-i-work/: a card per party with the switch and the default on the card, the identity summary, and the account-menu entry point. Every gap the prototype states is either closed by this story's server tasks or stated on the screen.
Build the Reporting lines screen DONE 2026-10-09 2026-10-09 Build the accepted variant A of doc/prototypes/membership/reporting-lines/: the indented tree with the selected person beside it, the manager picker that cannot offer a descendant, and the change reason. The absent approval is stated, not built.

5. Plan

The prototypes come first, because the screen decides what the server must serve. Both journeys carry gaps no screen can be sized around without seeing it: neither can name a party today, the reporting line has no tree read and no cycle guard, and a change to it has no approval. A prototype shows the shape, the copy and the empty states, and it makes the server work visible before anybody plans it.

# Step What it leaves behind
1 Scaffold the story and wire it into the programme The story, the two prototype tasks, the sprint row and the programme's order table.
2 Prototype Where I work The member's screen, three variants on one static page, each stating the gaps the journey records.
3 Prototype Reporting lines The administrator's tree, three variants on one static page, each stating the gaps the journey records.
4 The product owner signs both off The accepted variants, recorded on the two tasks and in this story's * Decisions.
5 Write the build tasks from the accepted prototypes One task per screen and one per server gap, each naming the accepted prototype as its design record.
6 Build the five server gaps A named party read for the member, a settable and clearable default party, a cycle guard on the reporting line, a reporting-line-only write, and a tree read.
7 Build the two screens, accepted variant A Where I work with its account-menu entry, and Reporting lines with the tree and the inspector.

6. Decisions

  • The accepted variants are A on both screens. Where I work puts a card per party with both actions on the card they change, so nothing depends on a selection made elsewhere. Reporting lines puts the indented tree beside the selected person, with the whole tenant on one screen and the controls in a fixed place. Each prototype task's * Result records what the accepted variant settles and what the rejected ones gave up.
  • The prototypes precede the build. A screen with three missing operations cannot be sized from its document alone. The prototype shows the layout, the copy and the empty states, and the build tasks are written from what the product owner accepts.
  • A prototype is a static page under doc/prototypes/membership/. React is for the product. The Prototypes page states the one way to add one, and nothing here adds a second.
  • A party reads as a name on both journeys. The account-party association carries party_id alone and no read outside sign-in turns it into a name. Both screens show a party, so the lookup is one server gap and not two, and the prototypes state the gap rather than inventing a name in a panel the server cannot fill.
  • The journey documents stay with the extraction pass. Where a prototype disproves a claim in a journey document, the correction is recorded on the task and on this story, and lands in the document with Extract the remaining component user journeys.
  • The reporting-line approval is stated, not built, unless the build carries it. The journey makes a change a proposal that a superior or a tenant administrator approves, and nothing in the platform holds an approval. The prototype states who approves and draws the line still, so the gap is visible in the design rather than discovered during the build.

7. Out of scope

  • The journey extraction, which is Extract the remaining component user journeys.
  • The other journey groups, Directory among them, each of which has its own story in the programme's order table.
  • The delivery machinery for an approval — a request, a queue, a notification and a decision — which is platform work rather than a Membership screen.

8. Documents

Document Kind Purpose
User Journeys knowledge The catalogue this story implements.
Membership journeys knowledge The group: the two journeys and the rules that frame them.
Choose where I work user_journey The member's journey, and the gaps the first prototype answers.
Draw the reporting line user_journey The administrator's journey, and the gaps the second prototype answers.
Prototypes knowledge The only inventory of prototypes, and the one way to add one.
TypeScript Clean Standard knowledge The checklist every TypeScript package in the tree passes, which the build tasks follow.

9. Result

Done 2026-10-09. Seven tasks, seven PRs, all merged.

Task PR What landed
Read a member's parties with their names #2974 iam.v1.ops.get_my_parties, named from IAM's party cache, with the stored default; reached at GET /api/me/parties and by a generated shell command.
Let a member set and clear their default party #2981 An empty party clears the default; the service stores "no default"; reached at POST /api/me/default-party.
Refuse a reporting line that would make a loop #2985 :prevent_cycle: on the reporting line, so the generated trigger walks the manager chain and refuses a ring.
Give the reporting line its own write #2987 iam.v1.ops.set_reporting_line: one field, the version the caller read, a reason, and a refusal on a stale version.
Read the reporting tree #2994 iam.v1.ops.get_reporting_tree: manager, depth and direct reports, for the tenant or one branch; reached at GET /api/reporting-tree.
Build the Where I work screen #2995 The member's screen on the accepted variant A, reached from the account menu.
Build the Reporting lines screen #2998 The administrator's screen on the accepted variant A, reached from the people list.

Every gap the two journeys record is closed or stated, which the acceptance requires:

  • Closed: a party now reads as a name on both screens; set-default has a route; clearing the default is expressible; the association read has a client wrapper; the reporting tree is one read rather than the browser nesting a flat roster; the server refuses a ring; the reporting line has a write that names one field and records a reason; the switch has a screen to use it from.
  • Stated on the screen: the office each person works in is not readable from the reporting screen, a reporting-line change is a proposal whose approval nothing holds, and the line has no history there.
  • Still recorded on the journey documents: the extraction pass owns those documents, so the two corrections the prototypes proved — the switch is wired end to end, and TopBar.tsx is not in the tree — stand recorded on the two prototype tasks for it to fold in.

Verification. Every task carries its own evidence: the IAM core suite (267 cases, 955 assertions at the last run of that task), the shell IAM suite (405 cases), the BFF membership cases (10), the web suite (479 tests in 46 files), the component-drift gate and clang-format. What the group did not do is walk either screen by hand against a running installation: the environment's database was recreated twice for SQL the group landed, and no tenant with members exists to sign in as. Both screens' data paths are covered end to end by the server suites, the BFF cases and the screens' own cases, and the walk is the one thing a reviewer should do before the group is called finished.

Emacs 29.3 (Org mode 9.6.15)