Skip to main content

Automation Persistence Analysis -- Product Owner Perspective

Role: Product Owner Date: 2026-02-13 Core Question: Does the platform need a separate automations collection (or equivalent first-class entity) to track autonomous execution chains over time, vs. keeping automations as entity_type: identity with identitySubtype?


Executive Summary

The founder's instinct is correct: the current data model cannot express "this automation" as a durable, trackable concept that persists across entity changes. But the fix is not a new collection -- it is a new entity subtype within the existing entities collection, combined with a chain snapshot mechanism that captures the full execution chain state at each sync. This gives us the stable identity the founder wants, the temporal tracking the CISO needs, and the listing/navigation the analyst requires -- without breaking the architectural decisions (ADR-002: single collection) that make the platform fast and simple.

This document lays out the user stories, the identity problem, the UI implications, the change detection UX, an option analysis with scoring, an MVP-vs-long-term phasing plan, and Neo4j compatibility for each option.

My recommendation: Option D (automation as a virtual entity) for Phase 1, combined with chain state snapshots (borrowing from Option C) for Phase 2. This is the only approach that delivers the founder's core insight -- automation chain continuity -- without introducing a parallel storage model that diverges from the entity graph.


Table of Contents

  1. User Stories
  2. The Identity Problem
  3. Listing and Navigation
  4. Change Detection UX
  5. Option Analysis
  6. MVP vs Long-term
  7. Neo4j Consideration
  8. Recommendation

1. User Stories

The founder's scenario is not hypothetical. It describes a real workflow that our target users (CISOs, security analysts, GRC leads) need daily. Here are the user stories that require "automation" as a first-class trackable entity, ordered by priority.

US-1: Automation History Timeline (P0 -- Demo Blocker)

As a CISO, I want to see the full history of an automation's access changes over time, so I can explain to the board how risk evolved and whether our controls caught it.

Scenario: The AzureGraphRouter chain was created 8 months ago with write access to incident only. Four months later, someone added hr_admin role to the ServiceNow integration user. Last month, the primary owner departed. The CISO needs to see:

  1. Month 0: Chain created. Access: incident table (write). Owner: Sarah Chen (active).
  2. Month 4: Role added: hr_admin. Access expanded: hr_case (CRUD), sys_user (read). No approval on record.
  3. Month 7: Owner Sarah Chen departed. Status: ownership_degraded.
  4. Month 8: Secondary owner team disbanded. Status: orphaned_ownership.

What breaks without automation persistence: Today, the CISO can see the current state of each entity independently. To reconstruct this timeline, they would need to:

  • Query the events collection for each of the 6+ entities in the chain
  • Mentally stitch together events from the business rule, script include, REST message, OAuth client, and service principal
  • Determine which events are relevant to this chain vs. other chains those entities participate in

This is not feasible in a demo. It is not feasible for an analyst under time pressure. It is not feasible for an auditor who needs deterministic, walkable evidence.

Acceptance criteria:

  • Automation detail page has a "Timeline" tab
  • Timeline shows all events affecting any entity in the chain, chronologically
  • Events are labeled with which entity changed and what changed
  • Milestones (first seen, role change, ownership decay) are highlighted
  • Timeline is exportable as part of the evidence pack

US-2: Automation Comparison (P0 -- Pilot Requirement)

As a security analyst, I want to compare an automation's state between two points in time, so I can determine whether a security incident was caused by a configuration change.

Scenario: A data breach occurred on January 15. The analyst needs to answer: "Was the AzureGraphRouter chain different on January 14 vs. January 16? Did something change that could have enabled the breach?"

What breaks without automation persistence: The existing temporal model (entity_versions + events) can reconstruct each entity's state at a point in time. But "the automation" is not an entity. To compare the chain at two points in time, the analyst would need to:

  1. Identify all entities in the chain at time T1 (but how? the chain is not stored)
  2. Reconstruct each entity's state at T1 and T2
  3. Diff each entity pair
  4. Determine whether the diff is security-relevant

Step 1 is the killer. Without a durable chain definition, the analyst has to discover which entities formed the chain at T1 -- and the chain composition itself may have changed. If the OAuth client was rotated between T1 and T2, the old client does not appear in the current chain. The analyst cannot discover it without manually querying events.

Acceptance criteria:

  • Automation detail page has a "Compare" feature
  • User selects two dates (or sync versions)
  • Platform shows the chain at each point in time, side-by-side
  • Added/removed/changed entities are highlighted
  • Changed properties (roles, permissions, credentials) are shown as diffs
  • Comparison is deterministic: same inputs always produce same output

US-3: Automation Inventory Listing (P0 -- Table Stakes)

As a security analyst, I want to see a list of all automations in the environment, sorted by risk, so I can prioritize which ones to investigate.

Scenario: The analyst opens the SecurityV0 dashboard. They need to see:

AutomationEntitiesTriggerDestinationOwnerRiskLast Changed
AzureGraphRouter6 entitiesincident (insert)graph.microsoft.comORPHANEDCritical2 days ago
HR Onboarding Flow4 entitieshr_case (insert)internalIT TeamMedium30 days ago
AI Triage via OpenAI3 entitiescatalog (submit)api.openai.comActiveHigh5 days ago

What breaks without automation persistence: Today, the Automations page lists individual identity entities. Each business rule, script include, and service principal appears as a separate row. The analyst sees ~92 rows of identities, not ~5-10 distinct automation chains. There is no way to say "these 6 entities are one automation" without a grouping concept.

The current security_relevance filter (active_external, dormant_authority, internal_inventory) helps reduce noise. But even after filtering, the analyst sees individual entities, not automation chains. The AzureGraphRouter chain appears as 4 separate rows (2 BRs, 2 SIs), each with partial information.

Acceptance criteria:

  • Automations page shows chains, not individual entities
  • Each row represents a logical automation (trigger -> code -> auth -> destination)
  • Columns include: chain name, entity count, trigger, destination, owner status, risk, last change
  • Clicking a row navigates to the automation detail page
  • Sorting by risk puts orphaned, high-blast-radius chains at the top

US-4: Automation Change Alert (P1 -- Continuous Monitoring)

As a security operations analyst, I want to be notified when an automation's execution chain changes (new permissions, rotated credential, new trigger), so I can assess whether the change introduces risk.

Scenario: The nightly sync detects that the ServiceNow integration user in the AzureGraphRouter chain was granted a new role (personalize). This role gives write access to sys_user profiles. The SOC analyst needs:

  1. Alert: "AzureGraphRouter chain changed: new role personalize on sn-integration-user"
  2. Context: What this means for the automation's blast radius
  3. Diff: Before (3 roles, 2 tables) -> After (4 roles, 3 tables, now including sys_user)
  4. Decision: Acknowledge, investigate, or escalate

What breaks without automation persistence: The events collection captures the role_assigned event on the ServiceNow integration user. But without a chain concept, the alert says "sn-integration-user gained role personalize" -- which is correct but lacks context. The analyst does not know that sn-integration-user is part of the AzureGraphRouter chain, which is orphaned, which reaches graph.microsoft.com. They have to manually trace the chain to understand the impact.

Acceptance criteria:

  • When any entity in a tracked automation chain changes, an event is attributed to the chain
  • Dashboard shows "N automations changed since last review" with a drill-down
  • Each change shows the affected chain, the specific entity that changed, and the before/after
  • Changes that expand blast radius or affect ownership are flagged as high-priority

US-5: Automation Drift Detection (P1 -- Compliance)

As a GRC lead preparing for SOC2 audit, I want to see which automations have drifted from their initial configuration, so I can document scope changes and remediation actions.

Scenario: During the SOC2 evidence collection period, the auditor asks: "Show me all automations where permissions expanded since the last review cycle." The GRC lead needs:

  1. List of automations where the chain's total blast radius increased
  2. For each: what was added, when, by whom, whether it was approved
  3. For each: current owner status and whether the owner reviewed the change

What breaks without automation persistence: Computing drift requires comparing the automation's blast radius at two points in time. Without a chain concept, the platform can only show drift per-entity: "sn-integration-user has 4 roles now, had 2 roles 6 months ago." The per-automation view -- "AzureGraphRouter now reaches 3 sensitive tables, up from 1" -- requires stitching individual entity drifts together, which is the chain definition problem again.

Acceptance criteria:

  • "Automation drift" report shows chains with expanded blast radius between two dates
  • Each entry shows: chain name, old blast radius, new blast radius, role/permission changes
  • Evidence export includes the chain-level drift as a sealed evidence pack section
  • Drift is computed deterministically from entity_versions + events

US-6: Automation Continuity Through Entity Rotation (P2 -- Architecture Completeness)

As a platform engineer, I want the automation concept to survive entity rotation (credential rotation, SP replacement, trigger table change), so that the CISO sees a continuous history even when underlying entities change.

Scenario: The security team rotates the OAuth client secret for the AzureGraphRouter chain. A new client secret entity is created; the old one is marked expired. From the CISO's perspective, nothing changed -- the automation still does the same thing, the same way, reaching the same resources. The automation's history should show "credential rotated on Date X" as a timeline event, not "old automation deleted, new automation created."

What breaks without automation persistence: If the automation concept is derived from entity relationships (Option A: computed views), credential rotation could cause the chain to "break" from the system's perspective. The old credential linked the SP to the REST message. The new credential links the same SP to the same REST message. But if the chain identity is computed from the exact set of entity IDs, the chain hash changes and it looks like a new automation.

Acceptance criteria:

  • Automation identity is based on the chain's structure (trigger + code + auth pattern + destination), not the exact entity IDs
  • Credential rotation appears as a timeline event on the existing automation, not as a new automation
  • SP replacement (same app registration, new principal) preserves automation continuity
  • The matching algorithm has documented, deterministic rules for what constitutes "same automation"

US-7: Cross-Automation Impact Analysis (P2 -- Enterprise)

As a security architect, I want to see which automations share entities (e.g., same SP, same credential, same trigger table), so I can assess cascading impact when one entity is compromised or modified.

Scenario: The security team discovers that sn-ticket-router SP credentials were exposed in a log file. They need to immediately know:

  1. Which automations use this SP?
  2. What is the combined blast radius?
  3. Which automation owners need to be notified?

What breaks without automation persistence: Without chains, the analyst can query "which entities reference sn-ticket-router" using existing relationship queries. But the answer is a flat list of entities, not a list of automation chains. The analyst has to manually group the entities into chains to determine the scope of impact.

Acceptance criteria:

  • Entity detail page shows "Used by N automation chains" with links
  • Impact analysis view shows all chains affected by a given entity
  • Combined blast radius across chains is computed and displayed
  • Shared entity changes generate alerts on ALL affected chains

2. The Identity Problem

The founder's core insight deserves a direct response:

"If at one point we find an automation with certain list of entities and connections, it has certain business logic and flow. Then some entities change, e.g., OAuth client ID is updated in the chain, but logic is the same. Originating trigger/schedule is the same, the final outcome is the same."

This is the Ship of Theseus problem for execution chains. The automation is a logical concept that transcends its component entities. Let me be precise about what constitutes "the same automation."

What Makes an Automation "The Same"

An automation's identity is defined by its structural pattern, not its component entity IDs. Specifically:

FactorPart of Identity?Why
Trigger table + event typeYes"What starts this chain" is core to its purpose
Entry-point code artifact (BR/Flow/Job)YesThe business logic that defines behavior
Destination endpoint patternYes"Where the data goes" defines the security boundary
Auth method (OAuth, SAML, API key)PartiallyThe auth pattern matters, but specific credential IDs can rotate
Specific credential entity IDNoCredentials rotate; the chain survives
Specific SP entity IDPartiallySP replacement is rare but possible; same appId = same automation
Roles/permissions on target identityNoThese change over time; changes are the drift we want to track
Owner entityNoOwnership changes are findings, not identity changes

The Matching Algorithm

To determine whether a chain discovered in scan N is "the same" as a chain from scan N-1, the platform should use a structural fingerprint:

fingerprint = hash(
trigger_table + trigger_event_type,
entry_point_source_id, // BR or Flow source system ID (stable)
destination_host, // graph.microsoft.com, api.openai.com, etc.
auth_pattern // "oauth2" or "api_key" or "saml"
)

If the fingerprint matches, the chain is the same automation. Entity-level changes (new credential, new role, new permission) are recorded as events on the automation timeline.

If the fingerprint changes (e.g., trigger table changed from incident to change_request), the platform records this as an automation modification event -- not a new automation. The automation keeps its ID, and the timeline shows: "Trigger changed from incident to change_request on Date X."

When Is It Truly a New Automation?

A new automation is created when:

  1. A new entry-point code artifact is discovered that was not seen before (new BR, new Flow)
  2. An existing entry-point is connected to a completely new destination (new REST message to a previously unknown host)

Deletion happens when:

  1. The entry-point code artifact is deleted or disabled in the source system
  2. The entire chain is disconnected (no path from trigger to destination)

This is the deterministic, explainable approach the platform's design constraints require.


3. Listing and Navigation

The Automations Page: Chains, Not Entities

Today, the Automations page is essentially a filtered Identities page. It shows individual entities with identitySubtype in automation subtypes. This is wrong from a user perspective.

Proposed Automations page structure:

+----------------------------------------------------------------------+
| AUTOMATIONS [Filter] [Show All] |
+----------------------------------------------------------------------+
| Sort by: Risk (desc) | Last Changed | Name |
+----------------------------------------------------------------------+
| |
| [!] AzureGraphRouter CRITICAL |
| incident (insert) -> Script Include -> graph.microsoft.com |
| 6 entities | 3 tables reachable | Owner: ORPHANED |
| Last change: Role added (hr_admin) - 2 days ago |
| |
| [!] AI Triage via Azure OpenAI HIGH |
| sc_request (submit) -> Flow -> api.openai.com |
| 3 entities | 1 table reachable | Owner: Active |
| Last change: 3 executions in 30d - stable |
| |
| [ ] HR Onboarding Workflow MEDIUM |
| hr_case (insert) -> Flow -> internal |
| 4 entities | 2 tables reachable | Owner: IT Team (active) |
| Last change: 30 days ago |
| |
+----------------------------------------------------------------------+
| Showing 5 of 12 automations (7 internal-only hidden) |
| [Show all automations including internal inventory] |
+----------------------------------------------------------------------+
Dashboard
|
+-- Automations (chain list) ---------> Automation Detail Page
| | |
| +-- [row click] +-- Overview tab (summary flow, L1)
| +-- Full Chain tab (all entities, L2)
| +-- Timeline tab (change history)
| +-- Graph tab (interactive, L3)
| +-- Findings tab (filtered to this chain)
| +-- Evidence tab (chain-level evidence pack)
|
+-- Identities (entity list) ---------> Entity Detail Page
| | |
| +-- [row click] +-- Properties tab
| +-- Relationships tab
| +-- Timeline tab (entity-level)
| +-- "Used by N automations" link
|
+-- Graph Explorer --------------------> Interactive graph
|
+-- Click automation node --------> Automation Detail Page
+-- Click entity node -------------> Entity Detail Page

Automations Page vs Identities Page: Separate

These must be separate pages, not merged. The reason:

  1. Different units of analysis. Automations page shows chains (grouped entities). Identities page shows individual entities. Merging them forces the user to switch mental models mid-page.

  2. Different sort criteria. Automations sort by chain-level risk (combined blast radius of all entities). Identities sort by entity-level properties (last activity, role count, ownership status).

  3. Different audiences. The CISO uses the Automations page for executive reporting ("we have 5 high-risk automation chains"). The analyst uses the Identities page for entity-level investigation ("show me all service principals with dormant authority").

  4. No duplication concern. The entity detail page links to its automation chain(s). The automation detail page links to its constituent entities. Cross-linking eliminates the need to show everything on one page.

Dashboard: Automation Risk vs. Entity Risk

The dashboard should show both perspectives:

+----------------------------------------------------------------------+
| RISK OVERVIEW |
+----------------------------------------------------------------------+
| |
| AUTOMATION CHAINS | ENTITY-LEVEL FINDINGS |
| 3 Critical (orphaned chains | 5 orphaned_ownership |
| with external egress) | 3 scope_drift |
| 2 High (active + elevated | 2 dormant_authority |
| permissions) | 1 privilege_justification_gap |
| 5 Medium (dormant authority) | |
| 7 Low (internal inventory) | |
| |
| TOP RISK: AzureGraphRouter | MOST AFFECTED: sn-integration-usr |
| (orphaned, 3 sensitive tables) | (4 findings, 3 chains) |
+----------------------------------------------------------------------+

The automation risk column is a rollup: the chain's risk is the maximum risk of any finding on any entity in the chain, adjusted upward if the chain is orphaned and has external egress.


4. Change Detection UX

When an automation's chain changes, the user experience must satisfy three personas:

For the SOC Analyst: Alert + Context

When a sync detects a change to any entity in a tracked automation chain:

ALERT: AzureGraphRouter chain changed

WHAT CHANGED:
Entity: sn-integration-user (ServiceNow identity)
Change: Role assigned: personalize (grants sys_user.write)
When: 2026-01-22T15:00:00Z
By: bob.chen@corp.com

IMPACT ON CHAIN:
Blast radius: 2 tables -> 3 tables (+sys_user [confidential])
New capability: write access to user profiles
Chain risk: HIGH -> CRITICAL (orphaned + sys_user access)

CHAIN CONTEXT:
AzureGraphRouter is an ORPHANED automation chain
Trigger: incident table (insert)
Destination: graph.microsoft.com (external)
Last owner: Sarah Chen (departed 2025-07-15)

[Acknowledge] [Investigate] [Escalate]

For the Security Analyst: Diff View

The automation detail page should offer a diff view between any two sync versions:

AZUREGRAPHROUTER - COMPARISON (v41 -> v42)

CHAIN STRUCTURE:
v41: incident -> BR -> SI -> REST -> OAuth -> SP (6 entities)
v42: incident -> BR -> SI -> REST -> OAuth -> SP (6 entities, same structure)

ENTITY CHANGES:
+ sn-integration-user: role_assigned: personalize
- Before: [itil, hr_agent_workspace, task_editor]
- After: [itil, hr_agent_workspace, task_editor, personalize]
- Added by: bob.chen@corp.com
- Approval: none on record

BLAST RADIUS DIFF:
+ sys_user table (confidential, contains PII)
- Actions: read, update
- Via roles: personalize

= incident table (internal) -- unchanged
= hr_case table (confidential) -- unchanged

FINDINGS DIFF:
+ NEW: scope_drift (4 roles, was 2 at initial setup, no re-approval)
= orphaned_ownership (unchanged, no new owner)

For the CISO: Timeline View

The timeline is the most important view for the CISO. It must show the automation's full history as a single, scrollable narrative:

AZUREGRAPHROUTER TIMELINE

2025-03-10 CHAIN CREATED
Trigger: incident table (insert)
Entry point: BR "Auto-route identity tickets via Entra"
Destination: graph.microsoft.com
Auth: sn-ticket-router (Entra SP) via OAuth2
Owner: Sarah Chen (primary), IT-Automation-Team (secondary)
Blast radius: 1 table (incident, internal)

2025-04-22 ROLE ADDED
Entity: sn-integration-user
Role: sn_incident_close
Approved: JIRA-IT-4521
Blast radius: unchanged (incident table, new action: close)

2025-07-15 OWNERSHIP DEGRADED
Owner: Sarah Chen -> DEPARTED (account disabled)
Finding: ownership_degraded
Secondary owner: IT-Automation-Team (still active)

2025-09-03 SCOPE DRIFT
Entity: sn-integration-user
Roles changed: 2 narrow -> 4 broad (itil, hr_agent_workspace,
personalize, task_editor)
No approval on record
Blast radius: 1 table -> 3 tables (+hr_case [confidential],
+all task tables)
Finding: scope_drift

2026-01-15 ORPHANED
Secondary owner: IT-Automation-Team -> DISBANDED
Finding: orphaned_ownership (no owner at any level)
Chain continues executing every 15 minutes

2026-01-22 CREDENTIAL ROTATED
Entity: OAuth client secret
Old key: key-abc (expired)
New key: key-def (expires 2026-07-15)
Chain structure: unchanged
Note: credential rotation with no owner review

This timeline is assembled from events across all entities in the chain, chronologically merged, with chain-level context (blast radius change, finding triggers) computed at each milestone.


5. Option Analysis

Scoring Criteria

I evaluate each option across 7 dimensions that matter for product delivery:

CriterionWeightWhy
User story coverage30%Can we ship the 7 user stories?
Time to demo20%How fast can we show the founder something?
Temporal fidelity15%Does it handle the Ship of Theseus problem?
Migration risk10%Does it require data migration or schema break?
Neo4j compatibility10%Does it align with the future graph index?
Operational simplicity10%Does it add operational burden?
Engineering effort5%How much code to write?

Option A: Keep Current Model, Add Computed Views

How it works: No schema change. Build "automation chain" as a computed view by traversing entity relationships (TRIGGERS_ON, CALLS, RUNS_AS, EXECUTES_ON, AUTHENTICATES_TO). The chain is discovered on every page load or API call. Timeline assembled from events of all entities in the chain.

Scoring:

CriterionScoreNotes
User story coverage4/10US-1 (timeline) possible but slow. US-2 (comparison) very hard -- must discover chain at T1 from T1 data. US-3 (listing) requires computing all chains on every page load. US-6 (rotation survival) fails -- chain identity computed from entity IDs.
Time to demo7/10No schema changes. Can build computed views quickly. But listing page is computationally expensive.
Temporal fidelity2/10Fatal flaw. If a credential rotates, the entity ID changes. The chain computed at T1 differs from the chain at T2. Cannot answer "was this the same automation?" without chain identity.
Migration risk10/10Zero migration. No schema changes.
Neo4j compatibility8/10Computed views translate naturally to Cypher queries.
Operational simplicity9/10No new collections, no new indexes.
Engineering effort6/10Moderate -- build chain discovery algorithm, timeline aggregator, listing page computation.

Weighted score: 5.3/10

Verdict: Option A fails on the founder's core requirement. "Show me the history of this automation chain" requires knowing what the chain was at each point in time. Computed views can only show the current chain, then find events for its current members. If the chain composition changed (credential rotation, SP replacement), the history is broken.


Option B: New automations Collection (Lightweight Reference)

How it works: New collection stores: automation_id, name, chain_definition (ordered list of entity references with roles), structural_fingerprint, first_seen_at, last_seen_at, risk_level. Entity changes tracked via existing entity_versions + events. Chain definition updated when the chain structure changes. Timeline assembled by joining the chain definition with events for all member entities.

Example document:

{
_id: "auto-azuregraphrouter-001",
tenant_id: "tenant-xyz",
name: "AzureGraphRouter",
structural_fingerprint: "sha256:abc...", // hash of trigger+entry+destination+auth_pattern
chain_definition: {
trigger: { entity_id: "uuid-incident-table", role: "trigger", table: "incident", event: "insert" },
entry_points: [
{ entity_id: "uuid-br-autoroute", role: "entry_point", subtype: "business_rule" },
{ entity_id: "uuid-br-autoroute-noown", role: "entry_point", subtype: "business_rule" }
],
code_artifacts: [
{ entity_id: "uuid-si-azuregraphrouter", role: "code", subtype: "system_execution" },
{ entity_id: "uuid-si-azuregraphrouternoowner", role: "code", subtype: "system_execution" }
],
auth_chain: [
{ entity_id: "uuid-rest-graph", role: "destination", host: "graph.microsoft.com" },
{ entity_id: "uuid-oauth-client", role: "credential", subtype: "oauth_app" },
{ entity_id: "uuid-sp-sn-ticket-router", role: "identity", subtype: "service_principal" }
]
},
computed_risk: "critical",
computed_blast_radius: { tables: 3, domains: ["it_ops", "hr"], sensitivity_max: "confidential" },
owner_status: "orphaned",
first_seen_at: ISODate("2025-03-10"),
last_seen_at: ISODate("2026-01-22"),
last_changed_at: ISODate("2026-01-22"),
sync_version: 42
}

Scoring:

CriterionScoreNotes
User story coverage7/10US-1 through US-5 covered. US-6 (rotation) requires updating chain_definition when entities change -- possible but needs careful matching logic. US-7 (cross-chain) easy with entity_id lookups.
Time to demo5/10New collection, new ingestion logic, new API endpoints, new UI pages. Medium effort before anything is visible.
Temporal fidelity6/10Chain definition provides stable identity. But the definition itself only captures current state -- to see what the chain looked like 6 months ago, you still need to reconstruct from entity_versions. The chain_definition can become stale if an entity is deleted and the automation doc is not updated.
Migration risk5/10New collection is additive (no existing data changes). But requires backfilling automation documents from existing entities. Requires new sync logic to update automations after each connector sync.
Neo4j compatibility6/10Automations collection is MongoDB-specific. Neo4j would need its own representation. Two sources of truth for chain structure.
Operational simplicity6/10New collection to monitor, index, backup. New staleness problem: chain_definition can diverge from actual entity relationships.
Engineering effort5/10Moderate-to-high: new collection, chain discovery logic, chain matching algorithm, ingestion hooks, API endpoints, UI pages.

Weighted score: 5.9/10

Verdict: Option B is a reasonable approach but introduces a parallel data model. The automations collection knows about entities, but entities do not know about automations. This creates a consistency problem: when an entity changes, who updates the automation document? When an automation is queried, the chain_definition may be stale. The temporal problem (what did the chain look like 6 months ago?) is only partially solved -- the chain_definition is a current-state reference, not a historical snapshot.


Option C: New execution_chains Collection (Rich Model)

How it works: Like Option B but stores full chain state snapshots at each sync. Each document captures the complete chain: all entities with their properties, relationships, computed blast radius, and risk assessment at a specific point in time.

Example document:

{
_id: ObjectId,
chain_id: "auto-azuregraphrouter-001", // Stable across syncs
tenant_id: "tenant-xyz",
sync_version: 42,
detected_at: ISODate("2026-01-22"),
structural_fingerprint: "sha256:abc...",

entities: [
{ entity_id: "uuid-br-autoroute", entity_type: "identity", properties: { /* snapshot */ }, role_in_chain: "entry_point" },
{ entity_id: "uuid-si-azuregraphrouter", entity_type: "identity", properties: { /* snapshot */ }, role_in_chain: "code" },
// ... all entities with full property snapshots
],
relationships: [
{ source_id: "uuid-br-autoroute", target_id: "uuid-si-azuregraphrouter", type: "CALLS", properties: { /* */ } },
// ... all relationships in the chain
],

computed_blast_radius: {
resources: [{ id: "uuid-incident", name: "incident", domain: "it_ops", sensitivity: "internal", actions: ["read", "update"] }],
total_resources: 3,
domains: ["it_ops", "hr"],
sensitivity_max: "confidential"
},
computed_risk: "critical",
owner_status: "orphaned",

// Diff from previous snapshot
diff_from_previous: {
entities_added: [],
entities_removed: [],
properties_changed: [{ entity_id: "uuid-sn-user", field: "relationships", change: "role_assigned: personalize" }],
blast_radius_change: { added_resources: ["uuid-sys-user"], removed_resources: [] }
}
}

Scoring:

CriterionScoreNotes
User story coverage9/10ALL user stories covered. US-2 (comparison) is trivial -- just load two chain snapshots and diff. US-6 (rotation) works because the chain snapshot captures the state at each sync. US-1 (timeline) assembled from diffs between snapshots.
Time to demo3/10Heaviest implementation. New collection, snapshot logic, diff computation, chain matching, API endpoints, UI.
Temporal fidelity10/10Full temporal history. Each sync produces a chain snapshot. Comparing any two points in time is a document query, not a reconstruction. The founder's scenario ("show me the history of this automation") is a direct query.
Migration risk4/10New collection, significant storage growth, complex sync logic. Backfilling requires running chain discovery against all historical entity_versions.
Neo4j compatibility5/10Chain snapshots are MongoDB-specific. Rich temporal model in MongoDB, thin graph in Neo4j. Two very different representations of the same concept.
Operational simplicity3/10Storage grows linearly with syncs x chains. Each sync creates N chain snapshot documents. At daily syncs with 50 chains, that is 18,250 documents per year per tenant. Small documents (~5-20KB each), but the collection grows fast. Also: complex sync logic to compute diffs.
Engineering effort3/10Highest engineering effort. Full-chain snapshot logic, diff computation, chain matching, storage management, API endpoints, UI.

Weighted score: 5.7/10

Verdict: Option C has the best temporal fidelity but the highest cost. It duplicates entity data that already exists in entity_versions. The diff computation (what changed between two chain snapshots) is the same work the events collection already captures at the entity level. The main value of Option C over Option B is pre-computed chain snapshots that make comparison trivial -- but this value can be achieved by caching chain state on demand rather than storing it at every sync.


Option D: automation as a Virtual Entity Type

How it works: Add a new entity_type: "automation" to the existing entities collection. The automation entity has properties including a chain definition (references to member entities), structural fingerprint, computed risk, and computed blast radius. It has CONTAINS relationships to each entity in its chain. It is versioned by the existing entity_versions machinery. It participates in the existing temporal model (events, baselines).

Example entity document:

{
_id: "uuid-auto-azuregraphrouter",
tenant_id: "tenant-xyz",
entity_type: "automation",
source_system: "servicenow", // Primary source system
source_id: "chain:auto-route-identity-tickets-via-entra", // Derived stable ID

properties: {
display_name: "AzureGraphRouter",
structural_fingerprint: "sha256:abc...",
chain_pattern: {
trigger_table: "incident",
trigger_event: "insert",
entry_point_source_id: "br-autoroute-uuid",
destination_host: "graph.microsoft.com",
auth_pattern: "oauth2"
},
entity_count: 6,
computed_risk: "critical",
computed_blast_radius: {
total_resources: 3,
domains: ["it_ops", "hr"],
sensitivity_max: "confidential"
},
owner_status: "orphaned",
first_seen_at: ISODate("2025-03-10"),
last_changed_at: ISODate("2026-01-22"),
execution_mode: "autonomous",
security_relevance: "active_external"
},

relationships: [
{ type: "CONTAINS", target_id: "uuid-br-autoroute", properties: { role: "entry_point", position: 0 } },
{ type: "CONTAINS", target_id: "uuid-br-autoroute-noown", properties: { role: "entry_point", position: 0 } },
{ type: "CONTAINS", target_id: "uuid-si-azuregraphrouter", properties: { role: "code", position: 1 } },
{ type: "CONTAINS", target_id: "uuid-si-azuregraphrouternoowner", properties: { role: "code", position: 1 } },
{ type: "CONTAINS", target_id: "uuid-rest-graph", properties: { role: "destination", position: 2 } },
{ type: "CONTAINS", target_id: "uuid-oauth-client", properties: { role: "credential", position: 3 } },
{ type: "CONTAINS", target_id: "uuid-sp-sn-ticket-router", properties: { role: "identity", position: 4 } }
],

execution_paths: [
// Inherited from the chain: union of all member identity execution paths
// Or: computed as the chain's net blast radius
],

sync_version: 42,
last_synced_at: ISODate("2026-01-22"),
created_at: ISODate("2025-03-10"),
updated_at: ISODate("2026-01-22")
}

Scoring:

CriterionScoreNotes
User story coverage8/10US-1 through US-5 covered by existing temporal machinery. US-6 (rotation) works because the automation entity persists; member entities change. US-7 (cross-chain) works via reverse CONTAINS lookups. US-2 (comparison) requires loading entity_versions for each member at T1 and T2 -- possible but not pre-computed.
Time to demo7/10Uses existing infrastructure. New entity type is a property value, not a schema change. Chain discovery logic needed, but rendering uses existing EntityDetailPage with a new entity_type-specific view.
Temporal fidelity7/10The automation entity itself is versioned. Changes to the CONTAINS list (entity added/removed from chain) are captured in entity_versions. But changes to member entities' properties (role added to SP) are captured on those entities, not on the automation entity. Timeline requires merging events from automation + all members.
Migration risk7/10No new collection. No schema changes. Just a new entity_type value ("automation"). Backfilling requires running chain discovery to create automation entities for existing chains.
Neo4j compatibility9/10Automation is a node in the graph, just like any other entity. CONTAINS edges connect it to members. Neo4j traversal works naturally. No separate representation needed.
Operational simplicity8/10Same collection, same indexes, same backups, same monitoring. Only concern: the entities collection now has a conceptual type that is derived (not from a source system), which is a new pattern.
Engineering effort7/10Chain discovery logic + automation entity creation in ingestion pipeline. New UI views for automation detail. Existing temporal, listing, and graph infrastructure reused.

Weighted score: 7.5/10

Verdict: Option D scores highest because it leverages the platform's existing architectural strengths (single collection, entity versioning, materialized paths, graph traversal) while providing a stable identity for the automation concept. The automation entity is "just another entity" that happens to have CONTAINS relationships instead of OWNED_BY or HAS_ROLE. The existing temporal model works. The existing graph visualization works. The existing API pagination/filtering works.


Option Comparison Summary

Criterion (Weight)A: ComputedB: automationsC: execution_chainsD: Virtual Entity
User stories (30%)4798
Time to demo (20%)7537
Temporal fidelity (15%)26107
Migration risk (10%)10547
Neo4j compat (10%)8659
Ops simplicity (10%)9638
Engineering effort (5%)6537
Weighted Total5.35.95.77.5

6. MVP vs. Long-term

Phase 1: MVP (2-3 weeks)

Goal: The founder can demo "this is an automation, here is its history, here is how it changed."

Deliver:

  1. Chain discovery algorithm in the ingestion pipeline. After each sync, traverse relationships to identify distinct execution chains. Use structural fingerprint for stable identity.
  2. Automation entity creation. For each discovered chain, upsert an entity_type: "automation" document in the entities collection with CONTAINS relationships to chain members.
  3. Automation list page. New Automations page that queries entity_type: "automation" with server-side filtering/sorting. Each row shows chain name, entity count, trigger, destination, owner status, risk.
  4. Automation detail page. New page (or new entity_type-specific view on EntityDetailPage) that shows:
    • Summary flow (Level 1: trigger -> code -> auth -> destination)
    • Chain members (Level 2: clickable list of all entities with roles)
    • Timeline (events from all member entities, chronologically merged)
  5. Automation change attribution. When an event is written for any entity, check if that entity is in any automation chain. If so, update the automation entity's last_changed_at and potentially computed_blast_radius.

What this does NOT include (deferred):

  • Full chain-state snapshots (Option C territory)
  • Pre-computed chain diffs between syncs
  • Automation-level findings (findings remain on entities)
  • Cross-chain impact analysis (US-7)

Estimated effort: 40-50 hours (1 developer, 2-3 weeks)

ItemEffortNotes
Chain discovery algorithm10-12hBFS from automation subtypes following TRIGGERS_ON, CALLS, RUNS_AS, AUTHENTICATES_TO, EXECUTES_ON. Structural fingerprinting.
Automation entity upsert in ingestion8-10hPost-sync hook. Match discovered chains to existing automation entities. Create/update.
Automations API endpoints4-5hGET /automations (list with filters), GET /automations/:id (detail), GET /automations/:id/timeline
Automations list page (UI)6-8hDataTable with chain-level columns. Reuse existing DataTable infrastructure.
Automation detail page (UI)8-12hSummary flow, member list, timeline tab. Reuse AutomationFlowDiagram components.
Tests6-8hUnit tests for chain discovery. Integration tests for automation entity lifecycle. UI tests for list/detail pages.

Phase 2: Chain State Snapshots (4-6 weeks after MVP)

Goal: The analyst can compare the automation at two points in time with a pre-computed diff.

Deliver:

  1. Chain state snapshot on the automation entity. After each sync, if any member entity changed, store a snapshot of the chain's current state (member entity IDs, their key properties, blast radius) as an entity_version of the automation entity.
  2. Chain comparison API. GET /automations/:id/compare?v1=41&v2=42 returns the diff between two versions.
  3. Comparison UI. Side-by-side view of two chain states with highlighted diffs.
  4. Automation-level findings. New finding types: automation_scope_drift (chain blast radius expanded), automation_ownership_cascade (all owners in the chain decayed), automation_credential_exposure (shared credential in multiple chains).

Estimated effort: 30-40 hours

Phase 3: Enterprise Features (Post-Pilot)

Goal: Cross-chain analysis, automation-level alerts, integration with ticketing.

Deliver:

  1. Cross-chain impact analysis (US-7). Entity detail page shows "Used by N automation chains." Impact analysis view.
  2. Automation change alerts. Configurable notifications when automation risk changes.
  3. Automation-level evidence packs. Evidence pack section for the automation as a whole (chain structure, timeline, blast radius change history).
  4. Remediation workflow. "Create ticket" button that generates a remediation ticket with automation context.

Estimated effort: 40-60 hours


7. Neo4j Consideration

Option A (Computed Views) + Neo4j

Computed views translate naturally to Cypher path queries. Neo4j's MATCH (trigger)-[:TRIGGERS_ON]->(br)-[:CALLS]->(si)-[:EXECUTES_ON]->(rest)... pattern is exactly what computed views do in application code. This is the most Neo4j-friendly approach.

Problem: Temporal queries in Neo4j are limited. Point-in-time chain reconstruction still requires MongoDB entity_versions. The "two source of truth" problem remains.

Option B (automations collection) + Neo4j

The automations collection is a MongoDB-only artifact. Neo4j would need its own representation of chain concepts. Either:

  • Duplicate the automations collection logic in Neo4j (complex, divergence risk)
  • Keep automations in MongoDB only, query chain structure from Neo4j edges (inconsistency risk)

Neither is clean.

Option C (execution_chains) + Neo4j

Full chain snapshots in MongoDB, thin graph in Neo4j. These are fundamentally different representations. The temporal richness of chain snapshots has no Neo4j equivalent. Neo4j would be used for traversal, MongoDB for temporal analysis. This works but increases the conceptual surface area.

Option D (Virtual Entity) + Neo4j

This is the cleanest Neo4j migration path.

The automation entity is a node in the graph. CONTAINS edges connect it to its members. When Neo4j is added:

  1. Automation node in Neo4j: (:Automation {id, name, risk})
  2. CONTAINS edges: (:Automation)-[:CONTAINS {role, position}]->(:Identity)
  3. Chain traversal: MATCH (a:Automation)-[:CONTAINS]->(e) RETURN a, collect(e)
  4. Cross-chain query: MATCH (a:Automation)-[:CONTAINS]->(shared)<-[:CONTAINS]-(b:Automation) RETURN a, b, shared

The automation entity participates in Neo4j graph traversal just like any other entity. No special handling needed. The StorageAdapter interface works unchanged -- it just upserts another entity type.

Temporal queries remain in MongoDB (entity_versions, events), which is the intended split anyway. Neo4j handles graph structure and traversal; MongoDB handles temporal depth and rich documents.


8. Recommendation

Primary: Option D -- Automation as Virtual Entity Type

I recommend adding entity_type: "automation" to the existing entities collection. This is not a collection-level change. It is a new value in the entity_type discriminator, with CONTAINS relationships to chain members.

Why this wins:

  1. It answers the founder's question directly. "It might be worth creating separate collection to track automations" -- the answer is no, but the underlying need (stable automation identity with history) is addressed by making the automation a first-class entity within the existing collection.

  2. It leverages existing infrastructure. Entity versioning, events, baselines, materialized paths, API pagination, DataTable, graph visualization -- all of it works with a new entity type without modification.

  3. It is the only option that works cleanly with Neo4j. An automation node in the graph with CONTAINS edges is the natural representation. No separate collection, no inconsistency.

  4. It ships fast. The MVP (chain discovery + automation entity + list page + detail page) can be built in 2-3 weeks by extending existing patterns, not building new ones.

  5. It respects ADR-002. The single-collection decision was made for good reasons (query performance, operational simplicity). Option D preserves those benefits.

Secondary: Add Chain State Snapshots in Phase 2

Borrow the temporal depth from Option C without the collection proliferation. When any member entity changes, update the automation entity's properties (blast radius, risk, member list) and let entity_versions capture the snapshot automatically. The existing versioning machinery handles point-in-time reconstruction.

For the comparison use case (US-2), Phase 2 adds a dedicated comparison API that loads automation entity_versions at two points and computes the diff. This is lightweight compared to a separate execution_chains collection.

What I Am NOT Recommending

  1. Not a new collection. Options B and C add collections that duplicate entity data, create consistency concerns, and complicate the Neo4j migration. The same goals are achieved within the existing collection.

  2. Not a computed-only view. Option A cannot solve the Ship of Theseus problem. An automation must have a stable identity that persists through entity rotation. Computed views do not provide this.

  3. Not "just" a connector change. The automation concept is a platform responsibility. Connectors discover individual entities and relationships. The platform's ingestion pipeline assembles those into chains and creates automation entities. This is consistent with ADR-005 (platform-only findings) -- the connector emits facts, the platform derives meaning.

Phasing Summary

PhaseWhat ShipsWhenEffort
Phase 1 (MVP)Chain discovery, automation entity type, list page, detail page with timelineWeeks 1-340-50h
Phase 2Chain state snapshots via entity versioning, comparison API/UI, automation-level findingsWeeks 4-830-40h
Phase 3Cross-chain impact, alerts, evidence packs, remediation workflowPost-pilot40-60h

Decision Required

This recommendation changes the data model in a meaningful way: adding a new entity_type value and introducing CONTAINS as a relationship from a derived entity to source entities. This requires:

  1. ADR approval. Write ADR-006: Automation Entity Type. Get architect sign-off.
  2. Data model doc update. Add "Automation" to the Entity Types section of 01-data-model.md.
  3. Connector interface clarification. Connectors do NOT emit automation entities. The platform creates them during ingestion post-processing.
  4. UI plan update. Add Automations page to the UI roadmap with mockups.

The founder asked for a separate collection. I am proposing a more integrated approach that achieves the same product outcome with lower risk and better long-term fit. This needs a conversation with the founder to validate that the user stories and UX proposed here match the vision.


Appendix A: The AzureGraphRouter Chain Through Each Option

Option A (Computed View)

User opens Automations page.
Platform runs BFS from all entity_type: identity where identitySubtype in [business_rule, flow_designer_flow, ...].
Discovers chain: BR -> SI -> REST -> OAuth -> SP.
Displays as a row.

User clicks "Compare to 6 months ago."
Platform runs BFS from same starting entities, but with entities at T1 state.
Problem: if OAuth client was rotated, the old client ID is not in the current chain.
Platform cannot discover it without querying events.
Comparison is incomplete.

Option B (automations collection)

User opens Automations page.
Platform queries automations collection.
Displays automation docs as rows.

User clicks "Compare to 6 months ago."
Platform loads automation doc (current chain_definition).
Loads entity_versions for each member at T1 and T2.
Problem: chain_definition only has current entity IDs.
If OAuth client rotated, old client not in chain_definition.
Must query events to discover it was a member at T1.
Comparison partially complete.

Option C (execution_chains)

User opens Automations page.
Platform queries execution_chains collection (latest version per chain_id).
Displays chain docs as rows.

User clicks "Compare to 6 months ago."
Platform loads two chain snapshots (v1 at T1, v2 at T2).
Both snapshots contain full entity state at their respective times.
Old OAuth client is in the T1 snapshot.
Comparison is complete and pre-computed.

Option D (Virtual Entity)

User opens Automations page.
Platform queries entities where entity_type: "automation".
Displays automation entities as rows.

User clicks "Compare to 6 months ago."
Platform loads automation entity_version at T1 and current.
T1 version has CONTAINS relationships with the members at T1.
T2 version (current) has CONTAINS relationships with current members.
If OAuth client rotated: the automation entity's CONTAINS list was updated
when the rotation happened (ingestion post-processing detected the new
credential and updated the CONTAINS list; entity_versions captured the change).

Comparison: diff the two CONTAINS lists + load each member's state at T1/T2.
For full chain state comparison (Phase 2): the automation entity's properties
include computed_blast_radius, which was snapshotted in entity_versions.

Appendix B: Relationship of Automation Entity to Existing Entity Types

                            entity_type: "automation"
|
CONTAINS relationships
|
+----------+----------+---------+----------+
| | | | |
trigger entry_pt code auth identity
(resource) (identity) (identity) (identity) (identity)
| | | | |
incident BR:Auto- SI:Azure OAuth SP:sn-ticket-
table route Graph Client router
Router

The automation entity sits "above" the individual entities in the graph hierarchy. It does not replace them. It groups them. The CONTAINS relationship is directional: automation -> member. This means:

  • Querying "all members of this automation" = follow CONTAINS edges forward
  • Querying "which automations is this entity part of" = reverse CONTAINS lookup
  • Graph visualization = automation node at the top with edges down to members
  • Existing entity_type queries (identities, resources) are unaffected

The automation entity does NOT participate in execution paths. It is not an identity that "executes." It is a grouping concept. execution_paths remain on the member identity entities. The automation entity's computed_blast_radius is the union of its members' execution paths, stored as a summary (not duplicated paths).


Appendix C: What Does NOT Change

To be clear about scope: this proposal does NOT change:

  1. ADR-002. Single entities collection. The automation entity lives in the same collection.
  2. ADR-001. MongoDB only. No new database.
  3. Connector interface. Connectors emit NormalizedGraph with nodes and edges. They do not know about automation entities.
  4. NormalizedGraph schema. No new node type. The platform creates automation entities from discovered chains.
  5. Entity versioning. entity_versions captures automation entity state just like any other entity.
  6. Events collection. Events are still written to individual entities. The automation entity's timeline is assembled by querying events for all member entities.
  7. Findings. Findings are still on individual entities. Automation-level findings (Phase 2) are additive.
  8. Evidence packs. Existing evidence packs are unchanged. Chain-level evidence (Phase 3) is additive.
  9. StorageAdapter interface. No new methods. getEntity, upsertEntity, queryEntities all work with the new entity type.
  10. Graph visualization. The automation entity appears as a node in the graph with CONTAINS edges to members. Dagre layout handles it as a regular node.

Appendix D: Risk Assessment

RiskLikelihoodImpactMitigation
Chain discovery algorithm misidentifies chainsMediumHighStructural fingerprint + manual override. Start with conservative matching (require all 4 components to match). Log unmatched chains for review.
Automation entity creates confusion with identity entityMediumMediumClear UI labels. Automation entity has a distinct badge, color, and icon. The Automations page shows only automation entities. The Identities page excludes them.
CONTAINS relationships add storage overheadLowLowEach CONTAINS relationship is ~50 bytes. A chain with 10 members adds ~500 bytes. At 100 automation entities, total overhead is ~50KB. Negligible.
Automation entity updates lag behind member entity changesMediumMediumPost-sync hook runs chain discovery on every sync. If a member entity changes, the automation entity's properties (blast radius, risk) are recomputed.
Community confusion about "entity_type: automation"LowLowDocument in ADR-006. The automation is a platform-derived entity, not a source system entity. Clear naming: "Automation Chain" in the UI, not "Automation Identity."
Founder disagrees with approach (wanted separate collection)MediumHighThis analysis argues the product outcome is the same. Schedule a 30-minute walkthrough of the demo plan + UI mockups before starting implementation.

Analysis complete. 7 user stories, 4 options evaluated, 3 phases defined. Recommended approach: entity_type "automation" in existing entities collection, shipped in 2-3 weeks as MVP, with chain state snapshots in Phase 2. Total estimated effort: 110-150 hours across 3 phases.