---
id: STATE-PFM-CSH-001
title: Console start-up and working context
object: DOM-PFM-CSH-001
product_area: platform
module: console-shell
revision: 1
status: draft
design_intent: confirmed
origin: >-
  services-platform:docs/security/console-authentication.md#Console
  initialisation (final acceptance 2026-10-10);
  react-console:docs/url-architecture.md#6. Tenant from the URL (decided by
  Andrew 2026-10-14; final acceptance 2026-10-15)
implementation_evidence: observed
source_sha:
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
states:
  - name: loading
    meaning: >-
      Session and context resolution are pending, with only the neutral boot
      presentation mounted.
  - name: signing-in
    meaning: The browser navigates to sign-in because no usable session remains.
  - name: unconfigured
    meaning: >-
      Required sign-in configuration is absent and the setup screen identifies
      it.
  - name: no-access
    meaning: The final context request was refused and the screen offers session exit.
  - name: failed
    meaning: >-
      Context resolution failed and the failure screen offers Retry and
      sign-out.
  - name: ready
    meaning: >-
      A context is held and the frame is mounted for the current working
      context.
  - name: changing-workspace
    meaning: >-
      Entry or programmatic switching sets a busy flag while keeping the frame;
      HQ departure does not set it.
  - name: ended
    meaning: >-
      Session exit or expiry navigates away; drafts are removed or parked
      according to the reason.
transitions:
  - from: loading
    to: unconfigured
    trigger: Boot finds missing sign-in settings.
    guard: Only outside demonstration mode.
    side_effects: []
    who: console
    provenance: observed
    source: >-
      react-console:lib/security-context/provider.tsx:boot
      (missingConfiguration)
  - from: loading
    to: signing-in
    trigger: Session resolution or context reading reports expiry.
    guard: No additional guard.
    side_effects:
      - Carry a safe return path and query into the sign-in address.
    who: console
    provenance: test
    source: >-
      react-console:e2e/dev/url-architecture.dev.spec.ts#a signed-out deep link
      goes through sign-in and comes back to the same page and query
  - from: loading
    to: no-access
    trigger: The final boot request returns a permission refusal.
    guard: No additional guard.
    side_effects: []
    who: Platform (Security)
    provenance: observed
    source: >-
      react-console:lib/security-context/provider.tsx:boot
      (PermissionDeniedError)
  - from: loading
    to: failed
    trigger: The context request fails outside the expected session or access cases.
    guard: No additional guard.
    side_effects: []
    who: Platform or network
    provenance: observed
    source: 'react-console:lib/security-context/provider.tsx:boot'
  - from: loading
    to: ready
    trigger: Boot returns a usable context.
    guard: >-
      The final boot context follows strict entry or preference fallback; root
      and HQ staff landing remove selection.
    side_effects:
      - Update or remove the remembered workspace as directed by boot.
      - Display an unavailable-workspace notice when boot supplies one.
      - Retain the strict address outcome for one gate consumption.
    who: console
    provenance: test
    source: >-
      react-console:lib/security-context/boot.test.ts;
      e2e/controlled/tenant-unavailable.controlled.spec.ts#falls back to the
      default tenant, with a word of explanation, rather than refusing access
  - from: failed
    to: failed
    trigger: Retry advances the boot-attempt counter.
    guard: No additional guard.
    side_effects:
      - >-
        Re-run boot while retaining the failure presentation until phase
        changes.
    who: person
    provenance: observed
    source: >-
      react-console:lib/security-context/provider.tsx:SecurityContextProvider
      (attempt)
  - from: ready
    to: changing-workspace
    trigger: Address entry or programmatic switching requests another workspace.
    guard: The provider is ready to request a context change.
    side_effects:
      - Reset the context notice.
    who: person
    provenance: observed
    source: 'react-console:lib/security-context/provider.tsx:openTenantId, leaveTenant'
  - from: changing-workspace
    to: ready
    trigger: 'Workspace address entry settles, or programmatic switching succeeds.'
    guard: Only the latest context-change ticket can install response data.
    side_effects:
      - >-
        Successful changed-workspace entry clears SWR data and remounts the
        keyed subtree.
      - Refused address entry retains the held context.
    who: console
    provenance: observed
    source: >-
      react-console:lib/security-context/provider.tsx:openTenantId (ticket),
      leaveTenant
  - from: changing-workspace
    to: failed
    trigger: The programmatic switch fails outside its unavailable-workspace fallback.
    guard: Only the current switch ticket changes the failed phase.
    side_effects: []
    who: console
    provenance: observed
    source: >-
      react-console:lib/security-context/provider.tsx:switchTenant (no screen
      calls it at the pinned commit)
  - from: ready
    to: ended
    trigger: The person signs out or the session emits an ending event.
    guard: No additional guard.
    side_effects:
      - 'Discard drafts, except expiry parks them for their owner.'
      - Explicit sign-out removes the remembered workspace.
      - Clear shared SWR answers.
    who: person or session
    provenance: observed
    source: >-
      react-console:lib/security-context/provider.tsx:signOut, onSessionEnded
      listener
  - from: no-access
    to: ended
    trigger: Sign-out is chosen from the refused boot screen.
    guard: No additional guard.
    side_effects:
      - Run the explicit sign-out cleanup sequence.
    who: person
    provenance: observed
    source: >-
      react-console:components/console/console-boot-screens.tsx:consoleBootScreens
  - from: failed
    to: ended
    trigger: Sign-out is chosen from the failed boot screen.
    guard: No additional guard.
    side_effects:
      - Run the explicit sign-out cleanup sequence.
    who: person
    provenance: observed
    source: >-
      react-console:components/console/console-boot-screens.tsx:consoleBootScreens
  - from: ready
    to: ready
    trigger: Staff leave a selected workspace to work in HQ.
    guard: A workspace remains selected and the departure ticket is still current.
    side_effects:
      - >-
        Await SWR clearing, remove the selected workspace and remount before the
        context refresh.
      - >-
        Preserve workspace memory and silently retain the snapshot if follow-up
        refresh fails.
    who: console
    provenance: observed
    source: 'react-console:lib/security-context/provider.tsx:leaveTenant'
  - from: failed
    to: ready
    trigger: Retry produces a usable context.
    guard: This boot attempt has not been cancelled.
    side_effects: []
    who: console
    provenance: observed
    source: >-
      react-console:lib/security-context/provider.tsx:SecurityContextProvider
      (boot)
  - from: failed
    to: no-access
    trigger: Retry ends in a context permission refusal.
    guard: This boot attempt has not been cancelled.
    side_effects: []
    who: console
    provenance: observed
    source: >-
      react-console:lib/security-context/provider.tsx:SecurityContextProvider
      (boot)
  - from: failed
    to: unconfigured
    trigger: Retry detects missing sign-in configuration.
    guard: This boot attempt has not been cancelled.
    side_effects: []
    who: console
    provenance: observed
    source: >-
      react-console:lib/security-context/provider.tsx:SecurityContextProvider
      (boot)
  - from: failed
    to: signing-in
    trigger: Retry finds no usable session or receives session expiry.
    guard: This boot attempt has not been cancelled.
    side_effects: []
    who: console
    provenance: observed
    source: >-
      react-console:lib/security-context/provider.tsx:SecurityContextProvider
      (boot)
invalid:
  - from: loading
    to: ready
    why: 'No usable context was returned, so production content remains unmounted.'
  - from: no-access
    to: ready
    why: >-
      This start-up has no retry from the refused phase; a new session or load
      is needed.
  - from: changing-workspace
    to: ready
    why: An older context-change ticket cannot install its answer.
concurrency: >-
  A monotonically increasing ticket orders workspace changes. Tabs hold
  independent contexts and share browser preferences only where storage does so.
  Expiry parks live drafts; explicit sign-out forgets them and the remembered
  workspace.
invariants:
  - INV-DOM-PFM-CSH-001-01
  - INV-DOM-PFM-CSH-001-02
  - INV-DOM-PFM-CSH-001-04
  - INV-DOM-PFM-CSH-001-05
evidence:
  - >-
    react-console at baf6dae:
    lib/security-context/{provider.tsx,boot.ts,boot.test.ts};
    components/console/console-boot-screens.tsx;
    e2e/dev/initialisation.dev.spec.ts.
---
# Console start-up and working context

## The Lifecycle

```mermaid
stateDiagram-v2
  [*] --> loading
  loading --> unconfigured: configuration absent
  loading --> signing_in: session unavailable
  loading --> no_access: context refused
  loading --> failed: context failure
  loading --> ready: usable answer
  failed --> failed: retry pending
  ready --> changing_workspace: workspace entry or programmatic switch
  changing_workspace --> ready: latest answer applied or refused
  changing_workspace --> failed: switch read failed
  ready --> ready: leave for HQ
  failed --> ready: retry succeeds
  failed --> no_access: retry refused
  failed --> signing_in: session expired
  ready --> ended: sign out or session ended
  no_access --> ended: sign out
  failed --> ended: sign out
  ended --> [*]
```

## Transitions Explained

The production gate begins in loading and resolves session and context. Retry repeats that work while the failure screen stays visible. Address entry and programmatic switching can set a busy overlay on a ready context. Leaving for HQ follows a ready-to-ready path, clearing the workspace locally before its refresh completes.

## What Cannot Happen

The gate does not mount content without a context. Older context-change answers cannot overwrite newer selections. A no-access start-up has no in-place retry control. The ready provider may retain the previous workspace during a refused entry, but the workspace gate withholds requested content until context matches.

## Concurrency and Timing

A monotonically increasing ticket orders workspace changes. Tabs hold independent contexts and share browser preferences only where storage does so. Expiry parks live drafts; explicit sign-out forgets them and the remembered workspace.
