---
id: WF-PFM-CSH-007
title: Assemble the navigation menu
trigger: >-
  Frame mounting, entitlement updates or placement revalidation changes
  navigation inputs.
product_area: platform
module: console-shell
revision: 1
status: draft
design_intent: proposed
implementation_evidence: observed
source_sha:
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
implements:
  - CAP-PFM-CSH-006
  - CAP-PFM-CSH-007
  - CAP-PFM-CSH-008
  - CAP-PFM-CSH-013
  - CAP-PFM-CSH-014
crosses:
  - SVC-PFM-CSH-console-shell
inference: []
idempotency: >-
  Registry, placement and entitlement inputs determine a repeatable menu
  computation.
concurrency: >-
  SWR shares placement data by workspace and recomputes the menu when data or
  entitlements change.
hops:
  - 'n': 1
    actor_or_service: console (shell mode)
    action: Select the HQ link set or workspace section set from shared shell mode.
    anchor: >-
      react-console:components/console/app-sidebar.tsx:MainNavigation;
      components/console/use-shell-mode.ts:useShellMode
    seam: sync
    on_failure: The mode computation has no recovery branch.
    left_inconsistent: Only one navigation mode is presented.
    observer: Person through the selected menu.
    provenance: observed
    source: 'react-console:components/console/app-sidebar.tsx:MainNavigation'
  - 'n': 2
    actor_or_service: Metadata (getCustomObjectsNavigation)
    action: >-
      Request Metadata’s navigation response for readable active placements and
      custom apps.
    anchor: 'react-console:lib/custom-objects/use-custom-navigation.ts:readNavigation'
    seam: sync
    on_failure: Attempt the definition-list fallback.
    left_inconsistent: Built-in entries remain available while placements load.
    observer: No dedicated observer.
    provenance: observed
    source: >-
      services-platform:modules/custom-objects/aws/api/custom-objects.routes.ts:getCustomObjectsNavigation
  - 'n': 3
    actor_or_service: 'Metadata (listCustomObjects, listCustomApps)'
    action: >-
      If navigation read fails, request definitions and apps together and
      evaluate local type grants.
    anchor: >-
      react-console:lib/custom-objects/use-custom-navigation.ts:readNavigation;
      lib/custom-objects/navigation.ts:canReadObject
    seam: sync
    on_failure: >-
      Do not retry errors or focus revalidation automatically; keep cache if
      present, otherwise use only built-ins.
    left_inconsistent: >-
      Definition permissions can hide placements that narrow record roles would
      otherwise allow.
    observer: No dedicated observer.
    provenance: observed
    source: >-
      react-console:lib/custom-objects/use-custom-navigation.ts:useCustomNavigation
      (quiet: no retry)
  - 'n': 4
    actor_or_service: console
    action: >-
      Append built-in placements, arrange custom-app groups and discard sections
      without a built entry or placement.
    anchor: >-
      react-console:lib/custom-objects/navigation.ts:customNavigation,
      mergeNavigation; components/console/nav-config.ts:hasBuiltItem
    seam: sync
    on_failure: The pure merge has no compensation path.
    left_inconsistent: The pinned application order still differs from the planning delta.
    observer: Person through merged navigation.
    provenance: test
    source: >-
      react-console:lib/custom-objects/__tests__/navigation.test.ts#appends to
      an existing app after its built-in items
  - 'n': 5
    actor_or_service: console
    action: >-
      Apply domain enablement and item or section permissions, removing sections
      with no surviving entry.
    anchor: 'react-console:lib/console/use-visible-nav.ts:visibleSections, visibleLinks'
    seam: sync
    on_failure: Filtering has no retry or compensation.
    left_inconsistent: Unconditional links rely on destination authority.
    observer: Person through visible destinations.
    provenance: test
    source: >-
      react-console:lib/console/__tests__/registries.test.ts#hides domains the
      user cannot access and keeps what they can
  - 'n': 6
    actor_or_service: console
    action: >-
      Offer settings links only when the area permission passes and at least one
      area destination remains visible.
    anchor: >-
      react-console:lib/console/use-visible-nav.ts:areaHasVisibleNavigation,
      visibleSettingsLinks
    seam: sync
    on_failure: The visibility predicate has no recovery branch.
    left_inconsistent: Direct entry can still reach an area with an empty menu.
    observer: Person through offered settings entries.
    provenance: test
    source: >-
      react-console:lib/console/__tests__/settings-area-menu.test.ts#does not
      offer Security or Maintenance to a person who holds only the areas' access
      keys
  - 'n': 7
    actor_or_service: console (designer)
    action: >-
      Revalidate definition, custom-app and navigation keys after designer
      changes.
    anchor: >-
      react-console:lib/custom-objects/use-custom-navigation.ts:revalidateCustomNavigation
    seam: event
    on_failure: >-
      No compensation follows rejected revalidation; prior cached placements may
      remain.
    left_inconsistent: >-
      Other tabs receive no designer broadcast and may update on reconnect or
      remount.
    observer: Administrator through the locally revalidated menu.
    provenance: observed
    source: >-
      react-console:lib/custom-objects/use-custom-navigation.ts:revalidateCustomNavigation
branches:
  - Settings destinations substitute their area-specific sidebar.
waits:
  - Built-in menu entries render before placements answer.
evidence:
  - >-
    react-console at baf6dae:
    components/console/{nav-config.ts,app-sidebar.tsx,use-shell-mode.ts};
    lib/console/{domains.ts,use-visible-nav.ts};
    lib/custom-objects/{navigation.ts,use-custom-navigation.ts,api.ts}.
  - >-
    services-platform at ad3f6a6:
    modules/custom-objects/aws/api/{custom-objects,custom-apps}.routes.ts.
controls: [TC-AVL-001, TC-AVL-004, TC-AVL-005, TC-AZ-001, TC-AZ-002, TC-AZ-003, TC-AZ-004, TC-AZ-005, TC-AZ-006, TC-CRY-001, TC-CRY-002, TC-CRY-003, TC-CRY-004, TC-DAT-001, TC-DAT-002, TC-DAT-003, TC-IAM-001, TC-IAM-002, TC-IAM-003, TC-IAM-004, TC-IAM-005, TC-IAM-006, TC-LOG-001, TC-LOG-004, TC-LOG-005, TC-MON-001, TC-MON-003, TC-PI-001, TC-PI-002, TC-PI-003, TC-PI-004, TC-PI-005, TC-PRV-001, TC-PRV-002, TC-TEN-001, TC-TEN-002, TC-TEN-003, TC-TEN-004, TC-VEN-001, TC-VEN-002, TC-VEN-003]
---
# Assemble the navigation menu

## What It Does

Navigation combines built-in sections, administrative areas, staff links and customer record placements. Context and entitlement changes recompute the result, and built-in entries do not wait for Metadata.

## The Flow

```mermaid
sequenceDiagram
  participant C as Console menu
  participant S as Metadata
  Note over C: frame or input changes
  C->>S: readable placements with definition fallback
  S-->>C: placements or retained cache
  Note over C: merge built sections and filter entries
```

## When Something Fails

A failed navigation read falls back to definition and custom-app lists. If fallback fails, no automatic error retry or focus revalidation runs. Cached data can remain; without it only built-in entries appear. Reconnect, remount and explicit designer revalidation can ask again. Designer mutation does not broadcast updates to other tabs.

## Open Design Questions

Customer visibility of planned entries remains open. Websites is not gated in the menu. The required Accounting ordering remains separate from the pinned implementation.
