Product Owner Analysis: Execution Flow Display & Identity/Automation Labeling
Role: Product Owner Date: 2026-02-13 Core Question: How should the platform display the full execution flow of automations, and how should it handle the identity vs. automation labeling confusion?
Executive Summary
The AzureGraphRouter problem reveals two distinct product failures: (1) the UI calls everything "identity" when users need to distinguish automation code from automation credentials from automation orchestration, and (2) the execution flow visualization shows a 5-card summary when the actual chain has 9+ steps. These are not cosmetic issues -- they directly undermine our demo and the "explainable, walkable evidence" promise. This document proposes a user-facing label taxonomy, a full-flow visualization design, MVP scoping for execution flow completeness, user stories, and an analysis of the completeness-vs-readability tradeoff.
1. Label Taxonomy for Users
The Problem in Concrete Terms
When Sergey clicks on "AzureGraphRouter" in the UI, the header reads:
AzureGraphRouter
identity | servicenow | RG3 | Valid | Bound | External
His immediate reaction: "This is a business rule, but here it says identity." He is right. AzureGraphRouter is a Script Include -- it is automation code, not an identity in any meaningful sense. Calling it "identity" is accurate in our data model (nodeType: autonomous_identity maps to entity_type: identity) but misleading to every human who reads it.
The data model conflation is intentional -- the architecture decision (see 01-data-model.md) models all autonomous executors as Identity subtypes because they participate in the same execution path graph. But the user-facing label does not have to match the storage type. We need a display layer that translates internal types to intuitive categories.
Proposed User-Facing Label System
I propose a three-category label system for the UI. These categories map to user mental models, not internal types.
Category 1: Automation Code
What the user thinks: "This is a script, a rule, a flow -- it is the program that runs."
| identitySubtype (internal) | User-facing label | Icon hint |
|---|---|---|
business_rule | Business Rule | gear/cog |
system_execution (script include) | Script Include | code brackets |
flow_designer_flow | Flow | workflow/arrows |
scheduled_job | Scheduled Job | clock |
Badge display: Replace the generic "identity" badge with the specific automation type. The AutomationTypeBadge component already exists and renders these subtypes -- but the EntityBadge on the EntityDetailPage still shows the generic entity_type. Fix: when entity_type === "identity" and identitySubtype exists, display the subtype badge instead.
Header example after fix:
AzureGraphRouter
Script Include | servicenow | RG3 | Valid | Bound | External
Category 2: Automation Credential
What the user thinks: "This is the identity or credential the automation uses to authenticate."
| identitySubtype (internal) | User-facing label | Icon hint |
|---|---|---|
service_principal | Service Principal | shield/key |
oauth_app | OAuth Client | lock |
machine_account | Machine Account | server |
pat | Access Token | token |
These ARE identities in the traditional security sense. The "identity" label is correct here. But we should still show the specific subtype prominently.
Header example:
sn-ticket-router
Service Principal | entra_id | Orphaned | Active
Category 3: Automation Target
What the user thinks: "This is what the automation talks to -- an API, a table, a service."
These are resource entities. The label "resource" is acceptable but we should show the resource type:
| resourceType (internal) | User-facing label |
|---|---|
rest_message | REST Endpoint |
table | Table |
api_endpoint | API |
Sergey's Simplification: "automation -> auth -> destination -> data domain"
Sergey proposed a simplified mental model:
AUTOMATION --> AUTH (e.g. SP) --> DESTINATION --> DATA DOMAIN
This maps cleanly to our three categories:
| Sergey's term | Our category | Example |
|---|---|---|
| Automation | Automation Code | AzureGraphRouter (Script Include) |
| Auth | Automation Credential | sn-ticket-router (Service Principal) |
| Destination | Automation Target | graph.microsoft.com (REST Endpoint) |
| Data Domain | (property on target) | identity_platform |
This simplification should drive the flow diagram's default view (see Section 2). The full provenance chain is behind one click.
Implementation Cost
The label change is purely UI. No data model changes required.
EntityBadgecomponent: Add logic: ifentity_type === "identity"andproperties.identitySubtypeexists, display the subtype-specific label instead of "identity". Estimated: 1 hour.EntityDetailPageheader: Same logic. UseAutomationTypeBadge(already exists) for automation entities. Estimated: 30 minutes.- Graph node labels: In
MiniGraphand the main Graph Explorer, node tooltips and labels should showidentitySubtypefor identity nodes. Estimated: 1 hour.
Total: 2-3 hours. This is a P0 fix for the next demo.
2. Full Flow Visualization: What Should the Entity Detail Page Show?
Current State
When you click AzureGraphRouter, the AutomationFlowDiagram component renders 5 cards:
TRIGGER (Unknown) --> AUTOMATION (AzureGraphRouter, 0 exec/30d, SI) --> CALLS (Graph - sn-ticket-rou..., graph.microsoft.com, External) --> RUNS AS (sn-ticket-router, entra_id, Valid) --> CAN ACCESS (1 resource, identity_platform)
This is a good summary, but it is incomplete and contains a lie ("Unknown" trigger). The actual chain in ServiceNow is:
incident/INC0010023 (trigger record)
--> BR: "Auto-route identity tickets via Entra" (business rule, fires on incident after-insert)
--> BR: "Auto-route identity tickets-Enta-no-own" (second business rule, same trigger)
--> SI: AzureGraphRouter (script include, called by BR)
--> SI: AzureGraphRouterNoOwner (second script include)
--> REST: "Graph - sn-ticket-router" (REST message to graph.microsoft.com)
--> OAuth: Azure Graph OAuth Client (authentication)
--> SP: sn-ticket-router (Entra SP, ORPHANED)
--> Record Changes: updates assignment_group, u_auto_routed, work_notes
--> setWorkflow(false) (suppresses downstream triggers)
Proposed Design: Two-Level Flow
I propose a two-level visualization that serves both Sergey's simplicity requirement and the security analyst's completeness requirement.
Level 1: Summary Flow (Default View -- Sergey's Model)
This is the existing 5-card flow diagram, but corrected:
TRIGGER AUTOMATION CALLS AUTH CAN ACCESS
[incident table] [AzureGraph- [graph.microsoft- [sn-ticket- [1 resource,
on: insert Router] .com] router] identity_
2 business Script Include REST Endpoint Service platform]
rules fire] 0 exec/30d External Principal
ORPHANED
Key changes from current:
- TRIGGER card shows the actual trigger table and event type (from BR's TRIGGERS_ON edge), not "Unknown"
- TRIGGER card notes "2 business rules fire" as secondary text
- Each card links to its entity detail page
- RUNS AS shows ORPHANED status prominently (red badge)
This is Sergey's "automation -> auth -> destination -> data domain" in card form.
Level 2: Full Provenance Chain (Behind "Show Full Chain" Toggle)
When the user clicks "Show full execution chain" (a toggle below the summary flow), the view expands to show every entity in the chain:
TRIGGER RECORD BUSINESS RULES SCRIPT INCLUDES REST MESSAGE OAUTH SERVICE PRINCIPAL RECORD CHANGES
[incident table] [Auto-route identity [AzureGraphRouter] [Graph - sn-ticket- [Azure Graph [sn-ticket-router] [assignment_group]
on: insert tickets via Entra] Script Include router] OAuth Client] Entra ID [u_auto_routed]
Business Rule Called by BR graph.microsoft.com client_id: a1b2.. ORPHANED [work_notes]
[Auto-route identity [AzureGraphRouterNoOwner] External No owners setWorkflow(false)
tickets-Enta-no-own] Script Include
Business Rule Called by BR
This view is a horizontally-scrollable chain. Each column groups entities of the same type. Edges between columns show CALLS, RUNS_AS, AUTHENTICATES_TO, and EXECUTES_ON relationships.
Level 3: Full Graph (Existing Graph Tab)
The Graph tab already exists. It shows the ReactFlow/Dagre visualization of the entity's neighborhood. This is the "power user" view for analysts who want to explore relationships interactively.
What Goes Where
| Information | Level 1 (Summary) | Level 2 (Full Chain) | Level 3 (Graph) |
|---|---|---|---|
| Trigger table + events | Shown | Shown with detail | Node |
| Business rules that fire | Count only ("2 BRs") | Each BR with name, table, events | Nodes + edges |
| Script includes called | The entity itself | All SIs with caller info | Nodes + edges |
| REST message + URL | Shown | Shown with HTTP methods | Node |
| OAuth client | Hidden | Shown with client_id | Node |
| Service principal | Shown (RUNS AS card) | Shown with ownership detail | Node |
| Record changes / mutations | Hidden | Shown | Not shown (no entity yet) |
| setWorkflow behavior | Hidden | Shown as property | Not shown |
| Ownership chain | Status badge only | Full owner list | OWNED_BY edges |
| Blast radius | Resource count | Resource list with domains | EXECUTION_PATH edges |
Where the Data Comes From
The current AutomationFlowDiagram component already fetches related entities via useEntityBatch(relatedIds). The Level 2 view requires:
-
Business rules: Already emitted by connector as
autonomous_identitynodes withidentitySubtype: "business_rule". Already linked via RUNS_AS and EXECUTES_ON edges. BUT: the BR-to-SI CALLS edge is NOT emitted by the transformer (see Issue 2 analysis below). The BRs connect to REST messages directly via EXECUTES_ON, not through SIs. -
Second script include (AzureGraphRouterNoOwner): Already emitted as a separate node. It has its own RUNS_AS and EXECUTES_ON edges.
-
Trigger table: Already emitted as a resource node. BRs have TRIGGERS_ON edges to it. The
AutomationFlowDiagramcomponent just needs to follow the chain backward from the SI to find the BRs that call it, then follow TRIGGERS_ON from those BRs. -
Record changes / local_mutations: Collected by the connector but NOT emitted as nodes or edges. The
local_mutationsdata is on theExecutionChainobject but is not transformed into NormalizedGraph entities. -
OAuth client: Already emitted as a node with AUTHENTICATES_VIA edges from the REST message.
Implementation Path for Level 2
The biggest gap is that the UI currently renders the flow from the perspective of a single entity. For Level 2, we need to render from the perspective of a chain -- which means following edges in both directions.
Option A: Client-side chain reconstruction.
The AutomationFlowDiagram already has all related entities via useEntityBatch. Extend extractFlowSteps to:
- Find BRs that EXECUTES_ON the same REST message as this SI
- Find TRIGGERS_ON edges from those BRs
- Reconstruct the chain from trigger -> BR -> SI -> REST -> OAuth -> SP
Pros: No API changes. Fast to implement. Cons: Requires multiple hops of entity batch fetching. May miss entities if the batch is not deep enough.
Option B: Server-side subgraph API.
Use the existing GET /api/v1/entities/:id/subgraph?mode=execution_flow&depth=3 endpoint. This already does BFS from the entity and returns the neighborhood.
Pros: Single API call returns the full chain. Server handles traversal. Cons: The subgraph may include entities not in the execution chain (e.g., other relationships).
Recommendation: Option B (subgraph API) for data, Option A (client-side) for rendering. Fetch the execution_flow subgraph, then reconstruct the chain client-side from the returned entities and edges. The subgraph API already exists and was built for exactly this purpose.
3. MVP for Execution Flow Completeness
What Is Missing and What Matters
| Missing Element | Status | P0 (Demo) | P1 (Pilot) | P2 (GA) | Why |
|---|---|---|---|---|---|
| Trigger table + event type on BRs | Data exists, UI does not show | P0 | - | - | "Unknown" trigger is embarrassing. The TRIGGERS_ON edge exists on BRs. The flow diagram just does not traverse backward to find it for SIs. |
| Business rules in the chain | Nodes exist in platform, not shown in flow | P0 | - | - | Sergey explicitly asked: "Auto-route identity tickets is a business rule, but here it is identity." BRs are in the graph but not in the flow diagram. |
| BR -> SI CALLS edge | NOT emitted by connector | - | P1 | - | The correlator builds this internally (indirect_callers_by_si) but the transformer collapses it. Emitting it would make the chain explicitly traversable. For now, client-side reconstruction from shared REST message targets is sufficient. |
| Second script include | Node exists, not shown in flow | P0 | - | - | AzureGraphRouterNoOwner is a separate node. The flow diagram only shows the entity you clicked on. Level 2 should show all SIs in the chain. |
| Record changes / local_mutations | Collected by connector, NOT emitted | - | - | P2 | The connector's correlator collects local_mutations (GlideRecord operations, setValue calls, setWorkflow). But these are not transformed into NormalizedGraph entities. Surfacing them requires: (a) new entity type or property, (b) transformer changes, (c) UI rendering. Significant effort. |
| Trigger examples (actual incident records) | Collected by connector, NOT emitted | - | - | P2 | The ExecutionChain has trigger_examples field. These are actual records (e.g., INC0010023) that fired the chain. Useful for evidence packs but not critical for the flow diagram. |
| HTTP method details on REST messages | Partially available | - | P1 | - | REST messages have HTTP methods (GET, POST) collected by the connector. Some are on the REST message node properties. Not shown in flow diagram. |
| "Unknown" trigger on Script Includes | Structural gap -- SIs don't have triggers | P0 | - | - | Script includes are libraries, not triggered entities. The flow diagram should not show a TRIGGER card for SIs. Instead, it should show the BRs that call this SI and THEIR triggers. This requires the chain reconstruction described in Section 2. |
| Sergey's simplified labels | Not implemented | P0 | - | - | The label taxonomy from Section 1. 2-3 hours. |
P0 Delivery Plan (8-12 hours total)
-
Fix labels (2-3h): Replace "identity" with subtype-specific labels across EntityDetailPage, EntityBadge, and graph nodes.
-
Fix flow diagram trigger card (3-4h): For Script Includes, traverse backward: find BRs that EXECUTES_ON the same REST message, then show their TRIGGERS_ON table as the trigger. Replace "Unknown" with "incident table (on insert)".
-
Add Level 2 toggle (3-5h): Add "Show full chain" toggle below the summary flow. Fetch execution_flow subgraph. Render all entities in the chain grouped by type in columns.
P1 Delivery Plan (additional 6-10 hours)
-
Emit BR->SI CALLS edge from connector (2-3h): The transformer's
_process_execution_chainalready creates BR and SI nodes with separate EXECUTES_ON edges to the REST message. Add an explicit CALLS edge from BR to SI. This makes chain traversal explicit. -
Add HTTP method details to REST message cards (1-2h): Show GET/POST/PATCH methods from the REST message's properties.
-
Improve ownership display in flow (2-3h): When RUNS AS shows an orphaned SP, the flow card should show a red warning with owner status and link to the finding.
-
Data domain badges on graph nodes (1-2h): Sergey requested "which data domains are touched" visible in the graph. Add domain badges to automation nodes.
4. User Stories
US-1: As a security analyst, I want to see what type of automation an entity is, so I can distinguish a script from a service principal at a glance.
Acceptance Criteria:
- When viewing any entity with
entity_type: identity, the header badge shows the specificidentitySubtype(e.g., "Script Include", "Business Rule", "Service Principal") instead of the generic "identity" label. - The
EntityBadgecomponent uses subtype-specific colors: blue for automation code (BR, SI, Flow, Job), purple for automation credentials (SP, OAuth), gray for machine accounts. - Graph nodes in MiniGraph and Graph Explorer show the subtype label in the node tooltip.
Priority: P0 Estimate: 2-3 hours
US-2: As a security analyst, I want the execution flow diagram to show the actual trigger that starts the chain, so I do not see "Unknown" for Script Includes.
Acceptance Criteria:
- When viewing a Script Include entity, the TRIGGER card in the
AutomationFlowDiagramshows the table and event type from the Business Rule that calls this SI. - If multiple BRs call this SI, the TRIGGER card shows the table and "N business rules fire" as secondary text.
- If no upstream BR is found, the TRIGGER card shows "No known trigger" (not "Unknown").
- The TRIGGER card links to the trigger table's entity page.
Priority: P0 Estimate: 3-4 hours
US-3: As a security analyst, I want to expand the execution flow to see every entity in the chain, so I can trace the full provenance from trigger to record changes.
Acceptance Criteria:
- Below the 5-card summary flow, a "Show full execution chain" toggle is visible.
- When toggled, the view expands to show all entities in the chain: trigger table, all business rules, all script includes, REST message, OAuth client, service principal.
- Entities are grouped by type in horizontal columns (matching the Dagre graph layout mental model).
- Each entity in the expanded view is clickable and navigates to its detail page.
- The expanded view collapses back with the same toggle.
Priority: P0 Estimate: 3-5 hours
US-4: As a CISO reviewing a demo, I want the automation detail page to clearly show the blast radius through Sergey's simplified model (automation -> auth -> destination -> data domain), so I can explain the risk in one sentence.
Acceptance Criteria:
- The summary flow diagram maps to Sergey's four-step model: Automation, Auth, Destination, Data Domain.
- The CAN ACCESS card shows the data domains (not just resource count) prominently.
- When the SP is orphaned, the RUNS AS card shows a red "Orphaned" badge with the departed owner's name.
- A one-sentence risk summary appears above the flow: "AzureGraphRouter executes as orphaned SP sn-ticket-router, calling graph.microsoft.com (External), accessing identity_platform resources."
Priority: P1 Estimate: 2-3 hours
US-5: As a platform developer, I want the connector to emit explicit BR->SI CALLS edges, so the platform can reconstruct execution chains without inference.
Acceptance Criteria:
- The transformer's
_process_execution_chainemits aCALLSedge from each Business Rule node to each Script Include node it invokes. - The edge carries properties:
callType: "direct"orcallType: "indirect". - The platform's path materializer follows CALLS edges during subgraph BFS traversal.
- At least 5 new unit tests cover CALLS edge emission for direct BR->SI and indirect BR->SI->REST chains.
Priority: P1 Estimate: 2-3 hours
5. Tradeoff: Completeness vs. Readability
The Tension
Sergey wants: "Graph needs to be readable as complexity grows." Security analysts want: "Every finding has a walkable evidence chain." The AzureGraphRouter chain has 9+ entities. Showing all of them in a linear flow diagram creates visual overload. Hiding some of them breaks the evidence promise.
My Position: Layered Disclosure, Not Simplification
I reject the idea that we must choose between completeness and readability. The right answer is layered disclosure -- show the simplified model by default, and let the user drill into the full chain on demand.
This is the same pattern used by:
- Network packet inspectors (Wireshark): Summary line by default, full packet decode on click.
- APM tools (Datadog, New Relic): Service map shows the simplified flow, trace view shows every span.
- Git:
git log --onelineby default,git log -pfor full diffs.
For SecurityV0, the layers are:
| Layer | What it shows | Who uses it | When |
|---|---|---|---|
| Level 1: Summary Flow (default) | Sergey's model: Automation -> Auth -> Destination -> Data Domain. 4-5 cards. | CISO, demo audience, executive review | First impression, triage, prioritization |
| Level 2: Full Chain (toggle) | Every entity in the execution chain, grouped by type. 8-12 cards in columns. | Security analyst, incident responder | Investigation, evidence review |
| Level 3: Interactive Graph (tab) | Full neighborhood graph with Dagre layout, clickable nodes, edge labels | Power user, graph analyst | Deep exploration, cross-chain analysis |
| Level 4: Evidence Pack (export) | Sealed, immutable JSON/Markdown with integrity hash | Auditor, compliance officer | Audit, legal, compliance evidence |
Specific Design Rules to Maintain Readability
-
No more than 5 cards in Level 1. Sergey's simplified model is exactly 4-5 steps. If the chain has more elements, they are collapsed into counts (e.g., "2 business rules" instead of showing each one).
-
Level 2 groups by type, not by sequence. Instead of a 12-card linear chain, Level 2 uses columns: one column for triggers, one for business rules, one for script includes, one for REST messages, one for auth, one for identity. This bounds the horizontal width and makes scanning easy.
-
Orphaned/warning entities get visual emphasis. An orphaned SP in the chain gets a red border. A dormant automation gets a yellow border. This focuses the eye on what matters without adding visual elements.
-
Cross-chain entities are shown once. If sn-ticket-router appears in 3 chains, it appears once in the graph with edges to all chains. This prevents duplication.
-
The graph tab has a complexity budget. If the neighborhood exceeds 50 nodes, the graph shows only the execution_flow subgraph (not the full neighborhood) and offers "Expand to full neighborhood" as a toggle. This prevents the "hairball" problem Sergey flagged.
What Happens as Complexity Grows
Today: 1 chain with ~9 entities. This is readable at all levels.
At 10 integrations per tenant: ~50-100 entities in the automation graph. Level 1 still works (each automation has its own summary flow). Level 2 still works (each chain is independent). Level 3 (graph) needs the complexity budget rule above.
At 100 integrations: The graph becomes a network, not a tree. At this point, the Level 3 graph needs filtering by risk group, data domain, or ownership status. The subgraph API already supports mode=execution_flow which returns only the execution chain, not the full neighborhood. This is the right default for Level 3 at scale.
The Line I Draw
Show everything the connector discovers. Never hide data. But control what is visible by default.
- Level 1 is the default. It is always readable.
- Level 2 is one click away. It is readable for chains up to ~15 entities.
- Level 3 is a tab. It is readable for graphs up to ~50 nodes.
- Level 4 is an export. It is complete regardless of size.
If someone asks "can you show me exactly how this automation reaches that resource," the answer must always be "yes" at some level of the UI. If the answer is ever "no, we simplified it away," we have failed the evidence-grade promise.
6. Synthesis: What Ships Next
Immediate (before next demo, 8-12 hours)
| # | Change | Hours | Clears |
|---|---|---|---|
| 1 | Replace "identity" badge with subtype-specific labels (US-1) | 2-3 | Sergey's labeling confusion |
| 2 | Fix trigger card for Script Includes (US-2) | 3-4 | "Unknown" trigger embarrassment |
| 3 | Add "Show full chain" toggle with Level 2 view (US-3) | 3-5 | Chain completeness |
Next sprint (P1, 8-12 hours)
| # | Change | Hours | Clears |
|---|---|---|---|
| 4 | Risk summary sentence above flow diagram (US-4) | 2-3 | CISO one-sentence explanation |
| 5 | Emit BR->SI CALLS edges from connector (US-5) | 2-3 | Explicit chain traversal |
| 6 | HTTP method details on REST message cards | 1-2 | Completeness |
| 7 | Data domain badges on graph nodes | 1-2 | Sergey's "which data domains" request |
| 8 | Graph complexity budget (50-node cap with expand toggle) | 2-3 | Readability at scale |
Deferred (P2, design now, implement later)
| # | Change | Dependency | Clears |
|---|---|---|---|
| 9 | Record changes / local_mutations as entities | Connector transformer changes | Full chain from trigger to data mutation |
| 10 | Trigger examples (actual incident records) | Connector transformer changes | Evidence-grade trigger proof |
| 11 | "Was -> is" temporal comparison in flow | Temporal model (Phase 4A) | Sergey's delta view |
| 12 | "Create Remediation Ticket" button (functional) | Ticketing integration | Sergey's remediation workflow |
7. Open Questions for Cross-Role Review
For Architect
Q: Should BR->SI CALLS edges use the existing relationship types or do we need a new one?
The data model defines RUNS_AS (identity binding), EXECUTES_ON (execution target), TRIGGERS_ON (trigger source), and AUTHENTICATES_TO (cross-system auth). None of these precisely describe "Business Rule calls Script Include." CALLS is a reasonable addition. But it could also be modeled as BR EXECUTES_ON SI (since the SI is what the BR executes). What is the correct semantic?
My preference: Add CALLS as a distinct relationship type. EXECUTES_ON means "executes actions on this resource." CALLS means "invokes this code." These are different semantics. A BR CALLS an SI; the SI EXECUTES_ON a REST message. The chain reads: BR -[CALLS]-> SI -[EXECUTES_ON]-> REST.
For Integrator
Q: The connector collects local_mutations and trigger_examples but does not emit them. What is the effort to emit them as NormalizedGraph elements?
For local_mutations, I would model them as properties on the automation node (not as separate entities). A new localMutations array property on the SI/BR node would contain {table, field, operation} tuples. The UI Level 2 view could render these as the "Record Changes" column.
For trigger_examples, I would model them as a triggerExamples property on the BR node. This is evidence, not a relationship. It goes in the evidence pack, not the graph.
For Developer
Q: The subgraph API returns a neighborhood BFS. Does it follow CALLS edges (once added) and TRIGGERS_ON edges? Or do we need to add these to the BFS traversal logic?
The subgraph API's execution_flow mode needs to be verified. If it only follows forward edges (entity -> RUNS_AS -> SP -> HAS_ROLE -> etc.), it will miss backward edges (BR -> TRIGGERS_ON -> table). The Level 2 view requires bidirectional traversal from the clicked entity: backward to triggers, forward to destinations.
Analysis: 5 sections, 5 user stories, 3 open questions. Total estimated P0 effort: 8-12 hours. This clears the labeling confusion and execution flow completeness gaps before the next demo.