Context Redesign — UI¶
Status: Design proposal — not yet implemented. Companion doc: context-redesign.md for the data model and backend.
1. New Top-Level Tab: "Contexts"¶
A new primary tab sits alongside Users, Resources, Systems, etc. It replaces the role the OrgChart tab plays today and subsumes the Risk-Scoring → Clusters page.
The tab has two panes:
- Left: tree selector. Lists every root context. Grouped by contextType. Each entry shows the tree's display name plus three visual signals: variant (border color), target type (badge), and system (subtle chip). Selector is collapsible per group.
- Right: the selected tree — switchable between tree view and list view, with a header card summarising provenance.
┌──────────────────────────────────────────────────────────────────────────┐
│ Contexts [+ New] │
├───────────────────────────────┬──────────────────────────────────────────┤
│ Trees │ Header: name • variant • target type │
│ │ • system • members • owner │
│ ▾ OrgUnit (Identity) 5 │ [Run now] [Edit] [Filter matrix] │
│ 🔵 Workday [Workday] ├──────────────────────────────────────────┤
│ 🔵 SuccessFact. [SAP HR] │ [Tree] [List] Filters ▾ │
│ 🟢 Manager hrc. [Entra P.] │ │
│ 🟢 Manager hrc. [Entra D.] │ Finance │
│ 🟫 Finance sub-teams │ ├─ Treasury │
│ │ │ └─ 🟫 Treasury automation │
│ ▾ AD-OU (Identity) 2 │ └─ Accounts payable │
│ 🟢 Prod Forest [AD-Prod] │ │
│ 🟢 DevTest [AD-Dev] │ │
│ │ │
│ ▾ Application (Resource) 3 │ │
│ 🟫 Procurement app │ │
│ 🟫 CRM platform │ │
│ 🟢 App grouping [Entra P.] │ │
│ │ │
│ ▾ ResourceCluster (Resource)1 │ │
│ 🟢 LLM clusters │ │
│ │ │
│ ▾ Tags 18 │ │
│ 🟫 priority (Identity) │ │
│ 🟫 legacy (Resource) │ │
│ … 16 more │ │
└───────────────────────────────┴──────────────────────────────────────────┘
1.1 Visual language¶
Three dimensions need to be distinguishable at a glance:
Variant (what produced this node) — shown as a left border color:
- 🔵 Blue — synced
- 🟢 Green — generated
- 🟫 Brown — manual
Target type (what the context contains) — shown as a pill/badge on the tree header and on each root entry: - 🟪 Identity (purple) - 🟧 Resource (orange) - ⚪ Principal (grey) - 🟨 System (yellow)
Scope system — shown as a muted chip [System name] next to the root display name, only when scopeSystemId is set. This is the thing that disambiguates two HR trees or two AD OU trees. For variant=manual with no scope set, the chip is absent. For a generated plugin run that ran across all systems, the chip is absent.
A manual sub-tree under a synced or generated parent shows up as 🔵 fading to 🟫 on the lineage — an analyst sees instantly which nodes they own versus which get overwritten on the next sync or plugin run.
1.2 Multi-tree handling¶
Several valid configurations need to be unambiguous in the selector:
| Configuration | How the selector handles it |
|---|---|
| Multiple synced HR trees (two Entra tenants, one CSV feed) | Three separate entries under "OrgUnit (Identity)", each with its own system chip. Display names can collide; system chip is the tiebreaker. |
Multiple AD OU trees from one ad-ou-from-dn plugin run per AD crawler |
Separate entries under "AD-OU (Identity)", each with its system chip. |
| System with no HR tree | Nothing implicit. The system just doesn't appear under any OrgUnit root. The Systems tab may show a "No org hierarchy synced" hint, but the Contexts tab does not fabricate one. |
| Generated tree spanning all systems (e.g. one global LLM cluster run) | Appears in its group with no system chip. |
| Duplicate plugin runs against the same system | A plugin run against a system where a previous run of the same plugin already produced a tree replaces the prior tree's contents (matched by sourceAlgorithmId + scopeSystemId). The selector does not show two entries. |
A filter bar above the selector lets the analyst narrow by target type, variant, or system — useful when there are dozens of trees. The selector remembers the last-expanded groups per user.
2. Tree View¶
Left pane = tree selector (one entry per root tree). Right pane = the selected tree.
Expand/collapse on parents. Hover shows direct & total member count. Right-click or "⋯" opens:
- View detail — opens the Context Detail Page (§5).
- Filter matrix by this node — sets a context filter and jumps to the Matrix tab.
- Add manual child (variant=manual only) — inline name prompt, then opens the new child for editing.
- Move (manual only) — reparent.
- Delete (manual only).
Synced and generated nodes are read-only except for grafting manual children under them.
3. List View¶
The same tree flattened into a table. Columns:
| Column | Notes |
|---|---|
| Name | Indented by depth for a visual hint even in list mode |
| Variant | Badge |
| Target type | Badge |
| Context type | Free-form sub-classification |
| Parent | Name of parent node (clickable) |
| Direct members | Count |
| Total members | Count incl. descendants |
| Last updated | For synced/generated, when the last run touched it |
| Actions | Same menu as right-click in tree |
Sortable and filterable. Useful for large flat trees (e.g. 2000-node AD OU structure) where the tree view is too dense.
4. Creating a Context Tree¶
The "+ New" button offers three entry points matching the three variants:
4.1 "Import tree" (synced)¶
Not done from the UI — synced trees appear automatically when a crawler (Entra, CSV) ingests them. The UI just explains this and links to the Crawlers page.
4.2 "Run plugin" (generated)¶
Modal wizard:
1. Pick plugin from the registered list, grouped by target type.
2. Configure parameters — form generated from the plugin's parametersSchema.
3. Preview — show how many contexts and members the plugin would create (dry-run API).
4. Run — kicks off a background run; user lands on the run-detail page with live progress.
After the run succeeds, the new tree shows up in the tree selector.
4.3 "Create manual tree" (manual)¶
- Pick target type.
- Pick context type (free-form, e.g. "Application", "BusinessProcess").
- Name + description.
- Lands on the empty root node. Analyst can add children and members via drag-drop from a side panel or a searchable picker.
5. Context Detail Page¶
Opens as a tab (following the existing pattern for User/Resource/AP detail pages). Deep-linked via #context:id.
Header card:
- Display name, variant badge, target-type badge, context-type
- Scope system chip (when scopeSystemId set)
- Owner chip (when ownerUserId set) — click to reassign (manual contexts only; others show owner as read-only)
- Provenance line: "Synced from System X on 2026-04-20 11:03" / "Generated by manager-hierarchy plugin on System X, run 2026-04-20 09:15" / "Created by wim@example on 2026-04-18"
- Action buttons: "Filter matrix by this" · "Run plugin again" (generated) · "Edit" (manual) · "Set parent" (manual) · "Set owner"
Sections (collapsible):
- Direct members — paginated, searchable list. For Identity/Principal contexts: columns name, UPN, department. For Resource contexts: columns name, type, system.
- All members (incl. descendants) — same shape, larger list.
- Sub-contexts — clickable list, same as the tree view for just this subtree.
- Matrix preview — embedded mini-matrix pre-filtered to this context. For Resource contexts: the assignments to these resources + the business roles that govern them. For Identity contexts: the resources these identities have access to.
- Risk summary — aggregate risk score for all members (replaces the risk-scoring Clusters page).
- Version history — the same trigger-based audit diff used elsewhere.
- Plugin run history (generated only) — list of runs with start/finish/status/counts.
6. Matrix Filtering¶
The Matrix toolbar gains a Context filter control beside the existing Tag / Type filters.
Widget:
Clicking the dropdown opens a tree picker with a search bar — users type "procurement" and pick a node from the flattened hits. Include-children toggle per filter. Multiple filters chip into an AND bar. "✕" removes one.
Behavior recap (see backend doc §5):
- Identity/Principal target → filters rows
- Resource/System target → filters columns
A small info icon next to each chip explains what got filtered, for clarity when someone's first filter hides half the columns.
7. Replacing the Risk-Scoring Clusters Page¶
The current Clusters page goes away. Its content becomes the Contexts tab filtered to contextType=ResourceCluster, variant=generated. The Risk Scoring page gets a "View clusters →" button that opens Contexts with that filter pre-applied.
Cluster ownership generalises — the ownerUserId column on Contexts is available on any context (synced, generated, manual). Clusters are one prominent consumer, but a team lead can equally own an "Application: Procurement" grouping or a manual sub-tree. Assigning an owner is an analyst action; it survives plugin re-runs because plugins never touch ownerUserId.
8. Tags UI¶
The tag UX does not change. Colored chips on rows, tag filter on list pages, "Manage tags" modal, bulk tag-by-filter — all identical to today. The analyst does not learn a new workflow.
Behind the scenes, each tag is a manual flat context with contextType='Tag', stored color in extendedAttributes.tagColor. This gives the analyst a second door into the same data, via the Contexts tab:
- "Tags" group in the tree selector lists every tag-context as a flat row. Opening one brings up the standard Context Detail Page.
- Give it a parent. The detail page "Set parent" action lets the analyst drag the tag-context under any other manual context. Once parented it behaves like any other sub-tree. The tag chip on list pages still works exactly as before — the chip doesn't care about hierarchy.
- Give it an owner. Same "Set owner" action as any other context. Useful for governance views and accountability.
- Demote back to flat. Clearing the parent returns the tag-context to the flat Tags group.
No "promote to hierarchy" wizard — parenting is just a field set. The legacy GraphTags / GraphTagAssignments tables are gone in v6; tag operations hit Contexts / ContextMembers directly.
9. Frontend Structure (proposed files)¶
New:
- app/ui/src/components/ContextsPage.jsx — tree selector + tree/list view switch
- app/ui/src/components/ContextDetailPage.jsx — detail tab (replaces the current minimal one)
- app/ui/src/components/contexts/ContextTreeView.jsx
- app/ui/src/components/contexts/ContextListView.jsx
- app/ui/src/components/contexts/ContextTreeSelector.jsx
- app/ui/src/components/contexts/NewContextModal.jsx — "+ New" dispatcher
- app/ui/src/components/contexts/RunPluginModal.jsx
- app/ui/src/components/contexts/CreateManualTreeModal.jsx
- app/ui/src/components/matrix/ContextFilterControl.jsx — the chip widget
Changed:
- app/ui/src/App.jsx — register the new tab, route #context:id
- app/ui/src/components/matrix/MatrixToolbar.jsx — embed ContextFilterControl
- app/ui/src/hooks/usePermissions.js — accept contextFilters query state and pass through
Removed (phase 4):
- app/ui/src/components/OrgChartPage.jsx — replaced by the Contexts tab
- The Clusters portion of app/ui/src/components/RiskScoringPage.jsx
10. Accessibility & Performance Notes¶
- Tree nodes are real
<button>elements for keyboard nav.aria-expandedon parents. - Large trees (10k+ nodes) render via the virtualised
@tanstack/react-virtualpattern already used in the matrix. - Color is never the only signal — variant and target-type badges carry text labels too.
- Tree → list toggle persists per-tree in localStorage.