Handbook
Org Explorer read strategies (OWA / Delve)
Read-only harvest for Outlook Web Org Explorer (Delve iframe). Cockpit orchestrates; LCDL provides governed tasks.
Updated
Entry
- OWA left nav → Org Explorer (not People for hierarchy harvest).
- People (
/people/) is a parallel LCDL surface for contact-list / directory-search ingest — see OUTLOOK-PEOPLE-READ-STRATEGIES.md. - Host URL:
outlook.office.com/host/{app-id}/… - Iframe:
loki.delve.office.com/orgexplorer/
CDP policy
- Reuse the operator's existing Org Explorer tab (
pick_org_explorer_cdp_page_target). - Attach with selective CDP (
connect_owa_page_via_selective_cdp) — applies focus emulation on connect (tab thinks it is active; no OS focus steal). - Read iframe via
Page.createIsolatedWorld+Runtime.evaluate. - Read-only clicks: person cards, View profile, Expand team (toggle on before snapshot), Show N more (repeat until exhausted), search picker, back/home.
- Keep
FORGE_LCDL_CDP_BRING_TO_FRONT=0(default) for unattended crawl; see EDGE-CDP-ATTACH.md.
Tasks
| Task | Purpose |
|---|---|
outlook_probe_org_explorer_health |
Iframe loaded, search box |
outlook_org_explorer_home_snapshot |
Focused person + visible hierarchy wire JSON |
outlook_org_explorer_prepare_focus |
Mail-first search / chart click → View profile (profile-enrich) |
outlook_org_explorer_open_profile_card |
View profile panel (email, LinkedIn if present) |
outlook_org_explorer_host_dr_panel |
Host flyout org structure when Delve iframe focus fails |
outlook_org_explorer_focus_search |
Search picker → confirmed personInFocusDescription focus (display_name, optional graph_id, optional mail) |
outlook_org_explorer_click_person |
Card click only when it changes focused person (not reports-list highlight) |
outlook_org_explorer_dismiss_profile_panel |
Close host profile flyout |
Input: { "session_ref": "…" } where the session page is a CdpOwaPageShim on an Org Explorer host tab.
Data sources (priority)
- Delve GraphQL (
useOrgExplorerQueryRef_OrganizationQuery) — recursive JSON walk personaphoto?aadObjectId=URLs — stablegraph_idpersonInFocusDescription-{aadObjectId}on focus cards — stablegraph_id- DOM
innerTextfallback — names/titles/locations only (nograph_id)
Not identity: blob: avatar photo URLs — session-ephemeral; never used as graph_id after harvest reconcile (reconcile_people_by_display_name).
Identity reconcile
After GraphQL + personaphoto collection, reconcile_people_by_display_name collapses duplicate keys sharing a normalized display_name to one canonical graph_id (GraphQL beats personaphoto beats focus card). Blob-only avatar rows are dropped when a real id exists for the same name.
Wire shape
{
"ok": true,
"focused_graph_id": "…",
"people": [{ "graph_id": "…", "display_name": "…", "job_title": "…" }],
"edges": [{ "src_graph_id": "…", "rel": "reports_to", "dst_graph_id": "…" }]
}
Edge rel values: reports_to, peer_of, works_with.
Consumer
forge-cockpit-web — cockpit_server/org/explorer_harvest.py, ingest.py. Operator flow: org-explorer-harvest.md. Dual architecture: ORG-EXPLORER-DUAL-ARCHITECTURE.md ↔ org-explorer-dual-architecture.md.