Implementation Plan — Entity Reclassification + Execution Chains + OAA Export
Date: 2026-02-14 Status: Active — Phases A0–A4 + B1 COMPLETE. Next: B2 (temporal chains) or D (UI/connector fixes). Supersedes: Previous version (Round 2 only). Now incorporates all 5 rounds of analysis plus plan review findings.
1. Decision Summary (Rounds 1-5)
Decisions Made
| Round | Decision | Status |
|---|---|---|
| Round 1 | Automation classification: execution_mode + security_relevance properties | SHIPPED (connector) |
| Round 2 | CALLS edge, UI label fixes, flow diagram, mutations | Approved |
| Round 3 | New execution_chains collection for chain persistence | Approved (6/6 unanimous in R4) |
| Round 4 | OAA is export format, not internal model; PO reverses R3 dissent | Approved (6/6 unanimous) |
| Round 5 | Reclassify entity types: automation, connection, credential | Approved (5/6 majority) |
The Four Workstreams
Workstream A: Entity Type Reclassification (~106h)
├─ Phase A0: ADRs + compatibility layer (accept old + new types)
├─ Phase A1: Platform types, storage, path materializer, evaluator
├─ Phase A2: Connector migration (emit new types + edges)
├─ Phase A3: UI + API endpoint updates
└─ Phase A4: Tests + architecture doc updates
└─ Must be done FIRST — chains should reference correctly-typed entities
Workstream B: Execution Chains Collection (~134h total: 94h Phase 1 + 40h Phase 2)
├─ Phase 1: Chain assembly + API + UI (current state + hash detection) — ~94h
└─ Phase 2: Temporal chain tracking (version history + diff API + events) — ~40h
└─ Depends on Workstream A — chain builder traverses mixed entity types
Workstream C: OAA Export Projection (~28h)
└─ Depends on Workstream A — OAA mapping uses correct entity types
└─ DEFERRED until customer/sales need
Workstream D: UI + Connector Fixes from Round 2 (~27h)
└─ Can run in parallel with B, some items absorbed by A
2. Workstream A: Entity Type Reclassification
Source: Round 5 synthesis (5/6 majority) Effort: ~106 hours (~3 weeks) Priority: P0 — do this first
Re-baseline note: Original estimate was 52h. Code review identified 13 identity-assumption locations across 47 impacted files. The developer audit (Round 5) estimated 136-168h including data migration. Since we have no active clients, data migration is unnecessary, but the base estimate needed correction to account for: (a) path materializer deep rework (14h), (b) dual-accept compatibility window, (c) API route refactoring, (d) ADR/doc sequencing (12h), (e) additional identity-assumption files found during post-fix review.
A.1 The Type System (NormalizedNodeType vs Internal Entity Type)
Critical distinction: NormalizedNodeType is the connector contract (what connectors emit). Internal entity_type is what the platform stores. They are not identical.
// CONNECTOR CONTRACT — what connectors emit in NormalizedGraph
export type NormalizedNodeType =
| "identity" // Things that authenticate: SP, OAuth App, machine account
| "automation" // Execution logic: BR, SI, Flow, Scheduled Job
| "connection" // Outbound configs: REST Message, SOAP, HTTP Connection
| "credential" // Auth material: OAuth Provider/Profile, API Key, Certificate
| "human_identity" // Human users — KEPT as connector type (see note below)
| "role" // Permission groupings (unchanged)
| "permission" // Individual capabilities (unchanged)
| "resource" // Data objects, tables, APIs (unchanged)
| "execution_evidence"; // Proof of execution (unchanged)
// PLATFORM INTERNAL — stored in entities collection
// entity_type values: identity, automation, connection, credential,
// owner (mapped from human_identity by normalizer),
// role, permission, resource, execution_evidence
Why
human_identitystays in NormalizedNodeType: The connector contract (05-connectors.md line 334) explicitly states: "Do NOT add 'owner' as a NormalizedNodeType. Ownership is a platform concept derived from connector-provided relationship data." The existing owner-normalizer already enriches human_identity nodes with owner semantics. We extend this pattern: connectors emithuman_identity, the platform normalizer maps to internalentity_type: "owner"during ingestion.
A.2 Artifact Reclassification Map
| Artifact | Current Type | New NormalizedNodeType | Internal entity_type | Subtype |
|---|---|---|---|---|
| Service Principal | autonomous_identity / service_principal | identity | identity | IdentitySubtype |
| OAuth App Registration | autonomous_identity / oauth_app | identity | identity | IdentitySubtype |
| Machine Account | autonomous_identity / machine_account | identity | identity | IdentitySubtype |
| Integration User | autonomous_identity / integration_user | identity | identity | IdentitySubtype |
| Business Rule | autonomous_identity / business_rule | automation | automation | AutomationSubtype |
| Script Include | autonomous_identity / system_execution | automation | automation | AutomationSubtype |
| Flow Designer Flow | autonomous_identity / flow_designer_flow | automation | automation | AutomationSubtype |
| Scheduled Job | autonomous_identity / scheduled_job | automation | automation | AutomationSubtype |
| REST Message | resource | connection | connection | ConnectionSubtype |
| OAuth Provider | autonomous_identity | credential | credential | CredentialSubtype |
| OAuth Profile | autonomous_identity | credential | credential | CredentialSubtype |
| Human User | human_identity | human_identity (unchanged) | owner (normalizer maps) | — |
A.3 Edge Type Updates
A.3.1 Edge Migration Mapping (Old → New)
The connector currently emits 10 edge types. Here is the explicit migration:
| Current Edge | Current Usage | New Edge | New Semantics | Migration |
|---|---|---|---|---|
| EXECUTES_ON (×6) | automation→REST message | INVOKES | automation→connection | RENAME when target is connection type |
| EXECUTES_ON (×6) | automation→resource | EXECUTES_ON | automation→resource | KEEP for automation→resource (e.g., table reads) |
| AUTHENTICATES_VIA (×2) | REST→OAuth | USES | connection→credential | RENAME |
| RUNS_AS (×5) | automation→SP | RUNS_AS | automation→identity | KEEP (type constraint changes) |
| RUNS_AS (×5) | flow→human user | RUNS_AS | automation→owner | KEEP (add owner as valid target) |
| TRIGGERS_ON (×3) | automation→table | TRIGGERS_ON | automation→resource | KEEP (type constraint changes) |
| OWNED_BY (×3) | entity→creator | OWNED_BY | any→owner | KEEP |
| AUTHENTICATES_TO (×2) | SP→SP cross-system | AUTHENTICATES_TO | identity→identity | KEEP |
| HAS_ROLE (×1) | identity→role | HAS_ROLE | identity→role | KEEP |
| GRANTS (×1) | role→permission | GRANTS | role→permission | KEEP |
| CREATED_BY (×1) | entity→creator | CREATED_BY | any→owner | KEEP |
| APPLIES_TO (×1) | permission→resource | APPLIES_TO | permission→resource | KEEP |
| (none) | — | CALLS | automation→automation | NEW (BR→SI) |
| (none) | — | AUTHENTICATES_AS | credential→identity | NEW (OAuth→SP) |
A.3.2 Complete Edge Type Reference
| Edge | From → To | Change | Notes |
|---|---|---|---|
| CALLS | automation → automation | NEW | BR invokes SI |
| INVOKES | automation → connection | NEW (replaces EXECUTES_ON for connection targets) | SI uses REST Message |
| USES | connection → credential | NEW (replaces AUTHENTICATES_VIA) | REST uses OAuth Profile |
| AUTHENTICATES_AS | credential → identity | NEW | OAuth represents SP |
| RUNS_AS | automation → identity | owner | Updated constraint | Automation executes as identity OR human user |
| TRIGGERS_ON | automation → resource | Updated constraint | Automation fires on resource events |
| EXECUTES_ON | automation → resource | Narrowed constraint | Automation reads/writes resource (kept for non-connection targets) |
| HAS_ROLE | identity → role | Unchanged | Identity has role |
| GRANTS | role → permission | Unchanged | Role grants permission |
| APPLIES_TO | permission → resource | Unchanged | Permission applies to resource |
| OWNED_BY | any → owner | Unchanged | Entity owned by human |
| CREATED_BY | any → owner | Unchanged | Entity created by human |
| AUTHENTICATES_TO | identity → identity | Preserved | SP→SP cross-system auth |
A.3.3 Compatibility Window
During migration, the platform must accept both old and new edge types:
// Dual-accept edge types in ingest.ts validator
const EDGE_TYPES = [
// New canonical types
"CALLS", "INVOKES", "USES", "AUTHENTICATES_AS",
// Preserved types
"RUNS_AS", "TRIGGERS_ON", "EXECUTES_ON",
"HAS_ROLE", "GRANTS", "APPLIES_TO",
"OWNED_BY", "CREATED_BY", "AUTHENTICATES_TO",
// Legacy aliases (accepted during migration, normalized on ingest)
"AUTHENTICATES_VIA", // → normalized to USES
"DELEGATES_TO", "APPROVED_BY", "MEMBER_OF", // kept for future connectors
] as const;
The ingestion pipeline normalizes legacy edges: AUTHENTICATES_VIA → USES (when source is connection, target is credential).
A.4 Corrected AzureGraphRouter Chain
[automation:business_rule] AzureGraphRouter
--TRIGGERS_ON--> [resource] incident table
--CALLS--> [automation:script_include] AzureGraphRouterSI
--INVOKES--> [connection:rest_message] Azure Graph REST
--USES--> [credential:oauth_profile] AzureGraphOAuth
--AUTHENTICATES_AS--> [identity:service_principal] SP-AzureGraph
--HAS_ROLE--> [role] Directory.Read.All
--GRANTS--> [permission] User.Read.All
--APPLIES_TO--> [resource] Graph API /users
A.5 Identity-Assumption Locations (Audited)
The following files contain hardcoded entity_type === "identity" assumptions that must be updated:
Core Platform (highest impact)
| File | Line(s) | What It Does | Effort |
|---|---|---|---|
src/ingestion/path-materializer.ts | 27, 169, 195 | Skips non-identity entities in path traversal; identity-check on RUNS_AS and AUTHENTICATES_TO targets | 12-16h |
src/workers/handlers/sync-ingestion.ts | 133 | Filters to identity-only before path materialization | 4h |
src/storage/mongo/adapter.ts | 629-633 | Execution flow traversal: assumes non-automation seed = identity for reverse edge logic | 4h |
src/workers/handlers/build-evidence-pack.ts | 44-60 | AUTHENTICATES_TO targets assumed to be identities; credential fetch logic | 3h |
src/evidence/sections.ts | 23 | EvidenceBuildContext interface hardcodes identity: EntityDoc as primary entity | 2h |
src/api/routes/paths.ts | 19, 82, 111 | API validates entity_type on blast-radius, accessible-by, cross-system-paths endpoints | 6h |
Ingestion + Normalization
| File | Line(s) | What It Does | Effort |
|---|---|---|---|
src/ingestion/types.ts | 1-26 | NormalizedNodeType and NormalizedEdgeType enum definitions | 2h |
src/api/routes/ingest.ts | 10-35 | NODE_TYPES and EDGE_TYPES validators reject new types | 2h |
src/ingestion/normalizer/owner-normalizer.ts | 26 | Gates on nodeType === "human_identity" for owner enrichment | 2h |
UI
| File | Line(s) | What It Does | Effort |
|---|---|---|---|
ui/src/hooks/use-automations.ts | 20 | Hardcodes entity_type: "identity" for automation queries | 2h |
ui/src/hooks/use-blast-radius.ts | 10 | URL hardcodes /identities/ path | 1h |
ui/src/hooks/use-cross-system.ts | 10 | URL hardcodes /identities/ for cross-system paths | 1h |
ui/src/components/graph/GraphNodeDetailsDrawer.tsx | 73 | isIdentity = entity?.entity_type === "identity" gates tabs/features | 1h |
Total audited locations: 13 files, ~42h effort within Workstream A.
Path materializer is the hardest change — it fundamentally assumes identity → role → permission → resource chains. With reclassification, the traversal must handle: automation → (CALLS) → automation → (INVOKES) → connection → (USES) → credential → (AUTHENTICATES_AS) → identity → role → permission → resource.
A.6 Implementation Tasks (Re-baselined)
| # | Task | Stack | Effort | Details |
|---|---|---|---|---|
| Phase A0: Foundation + Compatibility | 12h | COMPLETE (2026-02-14) | ||
| A0.1 | Create ADR-006 through ADR-009 | Docs | 3h | DONE — ADR-006 (entity reclassification), ADR-007 (execution relationship types), ADR-008 (execution chains), ADR-009 (OAA export) |
| A0.2 | Expand types.ts: ENTITY_TYPES 6→9, NormalizedNodeType +3, NormalizedEdgeType +4 | Platform TS | 2h | DONE — automation, connection, execution_evidence added to EntityType; identity, automation, connection added to NormalizedNodeType; CALLS, INVOKES, USES, AUTHENTICATES_AS added to NormalizedEdgeType |
| A0.3 | Expand ingest.ts validators: accept both old and new types/edges | Platform TS | 2h | DONE — NODE_TYPES +3, EDGE_TYPES +4 |
| A0.4 | Restructure identity-subtypes.ts: split into per-entity-type arrays | Platform TS | 2h | DONE — IDENTITY_SUBTYPES, AUTOMATION_SUBTYPES, CONNECTION_SUBTYPES, CREDENTIAL_SUBTYPES + combined ALL_NHI_SUBTYPES. pat moved to credential. Mongo adapter imports canonical source. |
| A0.5 | Update mapNodeType() + verify tests pass | Platform TS | 1h | DONE — 3 new cases in graph-transformer.ts. Return type simplified to EntityType. 207 unit + 96 integration tests pass. |
| Note: A0.4 in original plan (ingestion normalizer for old→new reclassification) deferred to Phase A1 — reclassifying autonomous_identity based on subtype requires path materializer updates first. | ||||
| Phase A1: Platform Core | 30h | |||
| A1.1 | Update StorageAdapter: handle new entity types in upsert, query, versioning | Platform TS | 8h | New indexes for automation, connection, credential |
| A1.2 | Rewrite path materializer for multi-type traversal | Platform TS | 14h | Follow CALLS→INVOKES→USES→AUTHENTICATES_AS→HAS_ROLE→GRANTS→APPLIES_TO; also RUNS_AS→identity|owner |
| A1.3 | Update finding evaluator: type-based rules | Platform TS | 4h | "Orphaned automation" not "orphaned identity"; adjust all type filters |
| A1.4 | Update sync-ingestion handler: remove identity-only filter | Platform TS | 4h | Path materialization for automation + identity types |
| Phase A2: Connector Migration | 16h | |||
| A2.1 | Update transformer.py: emit new node types + subtypes | Connector PY | 6h | automation, connection, credential types |
| A2.2 | Update transformer.py: emit new edge types | Connector PY | 4h | CALLS, INVOKES, USES, AUTHENTICATES_AS; keep RUNS_AS, TRIGGERS_ON, EXECUTES_ON |
| A2.3 | Update connector tests (247 tests) | Connector PY | 6h | Fix assertions for new types/edges |
| Phase A3: UI + API | 22h | |||
| A3.1 | Update API routes: entity-type-aware endpoints | Platform TS | 6h | /automations/, /connections/ or generic /entities/:id/ with type validation |
| A3.2 | Update UI entity pages: separate views per type | Platform UI | 8h | Identity/Automation/Connection/Credential pages, badges, icons |
| A3.3 | Update UI graph layout: layer assignment by entity type | Platform UI | 4h | Distinct shapes/colors for automation, connection, credential |
| A3.4 | Update UI hooks: use-automations, use-blast-radius | Platform UI | 4h | Correct entity_type filters, URL paths |
| Phase A4: Validation + Docs | 26h | |||
| A4.1 | Update/add platform unit tests | Platform TS | 6h | New type coverage, path materializer tests |
| A4.2 | Update platform integration tests | Platform TS | 6h | End-to-end ingest with new types |
| A4.3 | Update architecture docs (02-data-model, 01-database, 04-connectors, glossary) | Docs | 12h | Per architecture docs update plan Steps 2-6 (~11-14h). ADRs already created in A0.1. |
| A4.4 | Remove old type aliases after connector migrated | Platform TS | 2h | Clean up dual-accept |
| Total | ~106h |
A.7 Execution Order
✅ DONE Phase A0 — ADRs + compatibility layer (2026-02-14)
ADRs 006-009 created. Types expanded: EntityType 6→9,
NormalizedNodeType +3, NormalizedEdgeType +4. Subtypes split
into per-entity-type arrays. 207 unit + 96 integration tests pass.
Old connector still works. No behavior change.
✅ DONE Phase A1 — Platform core (2026-02-13)
Storage, path materializer, evaluator, sync handler.
Ingestion normalizer handles autonomous_identity reclassification.
Tests updated for new types.
✅ DONE Phase A2 — Connector migration (2026-02-13)
transformer.py emits new types + edges (CALLS, INVOKES,
USES, AUTHENTICATES_AS). 247 connector tests pass.
✅ DONE Phase A3 — UI + API (2026-02-13)
Canonical /entities/ routes with /identities/ aliases.
Entity type colors, blast radius eligibility expanded.
Graph filter sidebar + layout edge styles updated.
234 platform tests pass.
✅ DONE Phase A4 — Validation + docs (2026-02-13)
Architecture docs updated. Deprecation comments added.
Legacy type aliases kept for backward compat.
✅ DONE Phase B1 — Execution chains (2026-02-13)
Chain builder, storage adapter methods, API routes,
UI pages (list + detail). Chain assembly integrated
into sync pipeline.
A.8 Rollback Strategy
Each phase is independently rollbackable:
- A0 rollback: Remove new types from validators. No data change.
- A1 rollback: Revert storage/materializer. Old connector data still works (normalizer accepts old types).
- A2 rollback: Revert connector to emit old types. Platform normalizer handles them.
- A3 rollback: Revert UI/API. Backend still works with new types.
3. Workstream B: Execution Chains Collection
Source: Round 3 synthesis (6/6 unanimous after Round 4) Effort: ~94 hours Phase 1 + ~40h Phase 2 (~134h total) Priority: P1 — after Workstream A Dependency: Workstream A must complete first
B.1 New Collection Schema
// execution_chains collection
{
_id: "chain-uuid",
tenant_id: "...",
name: "AzureGraphRouter Incident Routing",
anchor_entity_id: "uuid-of-business-rule", // Stable identity root
entity_refs: [
{ entity_id: "uuid-br", entity_type: "automation", role: "entry_point" },
{ entity_id: "uuid-si", entity_type: "automation", role: "code_component" },
{ entity_id: "uuid-rest", entity_type: "connection", role: "outbound_target" },
{ entity_id: "uuid-oauth", entity_type: "credential", role: "auth_credential" },
{ entity_id: "uuid-sp", entity_type: "identity", role: "destination_identity" }
],
summary: {
trigger: "incident table insert",
destination: "graph.microsoft.com",
egress_category: "external",
blast_radius_domains: ["identity_platform"],
ownership_status: "orphaned",
total_roles: 4,
max_sensitivity: "confidential",
canonical_permissions: { // OAA enrichment (Phase 1.5)
reads: ["DataRead:incident"],
writes: ["DataWrite:user"]
}
},
composition_hash: "sha256:abc...", // Fingerprint for change detection
first_detected_at: ISODate("2026-02-12"),
last_seen_at: ISODate("2026-02-13"),
sync_version: 42
}
B.2 Chain Identity
Chain identity is anchored to the entry point (Business Rule, Flow, Scheduled Job):
const chainId = sha256(`${tenantId}:${anchorEntitySourceId}`).slice(0, 24);
Entity rotation (OAuth client_id change, SP credential rotation) does NOT break chain identity. The anchor entity persists → the chain persists.
B.3 Chain Assembly
Chains are assembled by the platform (not connectors) during sync pipeline:
Sync pipeline:
1. Receive NormalizedGraph
2. Diff engine → entity versions → events
3. Path materialization
4. ★ Chain assembly (NEW) ← BFS from entry points
5. Finding evaluation (now includes chain-level findings)
Chain builder algorithm:
- Find all entities with
entity_type: "automation"andautomationSubtypein["business_rule", "flow_designer_flow", "scheduled_job"] - BFS from each entry point following: CALLS → INVOKES → USES → AUTHENTICATES_AS → HAS_ROLE → GRANTS → APPLIES_TO. Also follow: RUNS_AS, TRIGGERS_ON, EXECUTES_ON for complete chain capture.
- Collect entity_refs with roles
- Compute composition_hash (SHA256 of sorted entity_id:role pairs)
- Upsert chain document (update if hash unchanged, create version if changed)
BFS edge traversal note: The BFS must follow ALL execution-relevant edges, not just the "happy path" chain. RUNS_AS captures identity binding (automation→identity or automation→owner). TRIGGERS_ON captures the trigger resource. EXECUTES_ON captures data resources read/written. These are all part of the chain's blast radius.
B.4 Phase 1 Implementation Tasks
| # | Task | Stack | Effort | Details |
|---|---|---|---|---|
| B1 | Define chain types: ExecutionChainDoc, ChainEntityRef, ChainSummary | Platform TS | 4h | TypeScript interfaces |
| B2 | Add execution_chains collection + indexes to StorageAdapter | Platform TS | 12h | 5 new methods (upsert, get, query, version, history) |
| B3 | Implement chain builder: anchor discovery + BFS + fingerprint | Platform TS | 16h | Core algorithm |
| B4 | Integrate chain assembly into sync pipeline | Platform TS | 8h | After path materialization |
| B5 | OAA canonical permission enrichment on chain docs | Platform TS | 4h | Phase 1.5 — map roles to DataRead/DataWrite |
| B6 | API routes: GET /execution-chains, GET /execution-chains/:id | Platform TS | 10h | List + detail endpoints |
| B7 | UI: ExecutionChainsPage (list) | Platform UI | 10h | DataTable with chain list |
| B8 | UI: ExecutionChainDetailPage (detail) | Platform UI | 10h | Chain visualization + entity list + summary |
| B9 | Add connector chain hints (optional chainMembership property) | Connector PY | 8h | Optional optimization |
| B10 | Unit + integration tests for chain assembly | Platform TS | 12h | Chain builder + API tests |
| Phase 1 Total | ~94h |
B.5 Phase 1 Execution Order
Week 1: B1 (types) → B2 (storage) → B3 (builder) → B4 (sync pipeline)
Week 2: B5 (OAA enrichment) → B6 (API) → B7 + B8 (UI) → B9 (connector hints)
Week 3: B10 (tests) + integration validation
B.6 Phase 2: Temporal Chain Tracking
Source: Round 3 synthesis, product feedback ("Delta view 'was → is' will be huge") Effort: ~40 hours Priority: P1 — ship within 2 weeks of Phase 1 Dependency: Phase 1 must complete first
Product requirement: Product feedback explicitly requests before-vs-after state comparison, temporal tracking of permission changes, and visual "was → is" representation on automations. Phase 1 can detect change (composition_hash differs) but cannot show what changed or when. Phase 2 closes this gap.
B.6.1 Phase 2 Schema Addition
// execution_chain_versions collection
{
_id: "version-uuid",
chain_id: "chain-uuid", // FK to execution_chains._id
tenant_id: "...",
version_number: 3,
entity_refs: [ /* snapshot at this version */ ],
summary: { /* snapshot at this version */ },
composition_hash: "sha256:def...",
created_at: ISODate("2026-02-14"),
sync_version: 43,
diff_from_previous: { // What changed
entities_added: [{ entity_id: "...", entity_type: "...", role: "..." }],
entities_removed: [{ entity_id: "...", entity_type: "...", role: "..." }],
summary_changes: {
blast_radius_domains: { added: ["storage"], removed: [] },
ownership_status: { from: "orphaned", to: "owned" }
}
}
}
// execution_chain_events collection
{
_id: "event-uuid",
chain_id: "chain-uuid",
tenant_id: "...",
event_type: "chain_entity_added" | "chain_entity_removed" | "chain_created"
| "chain_blast_radius_changed" | "chain_ownership_changed",
timestamp: ISODate("2026-02-14"),
details: { /* event-specific payload */ },
sync_version: 43
}
B.6.2 Phase 2 Implementation Tasks
| # | Task | Stack | Effort | Details |
|---|---|---|---|---|
| B11 | Add execution_chain_versions collection + indexes | Platform TS | 8h | Version storage + queries |
| B12 | Chain versioning: create version when composition_hash or summary changes | Platform TS | 10h | Integrated into chain upsert |
| B13 | Chain diff API: GET /execution-chains/:id/diff?from=ts1&to=ts2 | Platform TS | 6h | Compare two versions |
| B14 | Chain events: emit events on structural/summary changes | Platform TS | 6h | chain_created, chain_entity_added, etc. |
| B15 | UI: Chain temporal comparison tab (was→is view) | Platform UI | 10h | Visual diff of chain state over time |
| Phase 2 Total | ~40h |
B.6.3 Phase 2 Execution Order
Week 4: B11 (version storage) → B12 (version creation) → B13 (diff API)
Week 5: B14 (events) → B15 (temporal UI)
4. Workstream C: OAA Export Projection (DEFERRED)
Source: Round 4 synthesis (6/6 unanimous) Effort: ~28 hours Priority: P2 — deferred until customer/sales need Decision: OAA is an export format, not the internal data model
C.1 Architecture
Internal model (entities + execution_chains)
→ Export layer
→ OAA Application JSON (per source system)
→ SCIM User/Group JSON (future)
→ Neo4j Cypher (future)
C.2 OAA Export Design
- Each source system → one OAA Application (ServiceNow App, Entra App)
identityentities → OAAlocal_userautomationentities → OAAresource(type: automation)connectionentities → OAAresource(type: connection)credentialentities → custom properties on OAAlocal_userrole→ OAAlocal_rolepermission→ OAAcustom_permission(mapped to 10 canonical types)- Chain membership →
sv0_chain_membershipscustom property
C.3 The "Execute" Permission Extension
OAA's 10 canonical permissions lack "Execute." Add sv0_action_type extension:
{
"permission_type": ["NonData"],
"custom_properties": {
"sv0_action_type": "execute_automation",
"sv0_action_detail": "business_rule_trigger"
}
}
C.4 Implementation Tasks (when triggered)
| # | Task | Effort |
|---|---|---|
| C1 | OAA export endpoint: GET /api/v1/export/oaa/:sourceSystem | 12h |
| C2 | Entity → OAA type mapper | 4h |
| C3 | Chain → OAA chain registry resource | 4h |
| C4 | Tests + documentation | 4h |
| C5 | Optional: Veza push script | 4h |
| Total | ~28h |
5. Workstream D: UI + Connector Fixes (Round 2)
Source: Round 2 synthesis Effort: ~27 hours Priority: P1 — can run in parallel with B after A completes Note: Some items from original plan are now absorbed by Workstream A
D.1 Items Absorbed by Workstream A
| Original Item | Now Part Of |
|---|---|
| 2.5A: Fix entity labels | A3.2 (UI entity pages — types now correct, labels natural) |
| 2.5I: 9-layer graph layout | A3.3 (graph layout by type — layers from entity_type) |
D.2 Remaining Items
| # | Item | Stack | Effort | Status |
|---|---|---|---|---|
| D1 | Add CALLS relationship type | Connector + Platform | 3h | Part of A0.2 + A2.2 |
| D2 | Fix flow diagram trigger for Script Includes | UI | 3-4h | Pending |
| D3 | Add "Show full chain" toggle (Level 2 view) | UI | 3-5h | Pending |
| D4 | Fix 22 "unknown" execution_mode flows | Connector | 2h | Pending |
| D5 | setWorkflow(false) warning badge | Connector + UI | 2h | Pending |
| D6 | Local mutations + Effects tab | Connector + UI | 4h | Pending |
| D7 | Trigger examples display | Connector + UI | 3h | Pending |
| D8 | Data domain badges on graph nodes | UI | 1-2h | Pending |
| D9 | Promote sensitive-data flows to dormant_authority | Connector | 2h | Pending |
| Total | ~23-27h |
6. Master Schedule
Week 1: WA Phase A0 — ADRs + compatibility layer (12h)
Week 1-2: WA Phase A1 — Platform core (30h)
Week 2-3: WA Phase A2 — Connector migration (16h)
Week 3: WA Phase A3 — UI + API (22h)
Week 3-4: WA Phase A4 — Tests + docs (20h)
Week 4-6: WB Phase 1 — Execution chains (94h)
Week 4-5: WD — UI/connector fixes in parallel (27h)
Week 6-7: WB Phase 2 — Temporal chain tracking (40h)
Deferred: WC — OAA Export (28h, when needed)
Milestone Timeline
| Milestone | Week | Deliverable |
|---|---|---|
| M0: Compatibility | End of Week 1 | Old + new types accepted. ADR-006/007 published. Zero behavior change. |
| M1: Correct Types | End of Week 3 | All entities correctly typed. NHI count accurate. Connector emitting new types. |
| M2: Full UI | End of Week 4 | UI shows separate entity pages. Graph layered by type. Tests green. Docs updated. |
| M3: Chain Assembly | End of Week 5 | Chains discoverable via API. Stable chain IDs. Chain UI. |
| M4: Full Demo | End of Week 6 | End-to-end: correct types → chain list → chain detail → finding → evidence |
| M5: Temporal Chains | End of Week 7 | Chain versions, diff API, "was→is" UI. Product feedback addressed. |
| M6: OAA Export | When needed | GET /export/oaa/:sourceSystem endpoint |
Total Effort
| Workstream | Effort | Weeks |
|---|---|---|
| A: Entity Reclassification | ~106h | 3-4 |
| B Phase 1: Execution Chains | ~94h | 2.5 |
| B Phase 2: Temporal Tracking | ~40h | 1.5 |
| D: UI/Connector Fixes | ~27h | 1 (parallel) |
| C: OAA Export (deferred) | ~28h | 1 |
| Total (A+B+D) | ~267h | ~7 weeks |
7. Success Criteria
After Workstream A Phase A0 (Compatibility)
- Platform accepts both old and new node/edge types without rejection
- ADR-006 and ADR-007 published
- All existing tests pass unchanged (zero behavior change)
After Workstream A Complete (Entity Reclassification)
GET /entities?entity_type=identityreturns ONLY authenticating entities (SPs, OAuth Apps, machine accounts)GET /entities?entity_type=automationreturns Business Rules, Script Includes, Flows, JobsGET /entities?entity_type=credentialreturns OAuth Providers/ProfilesGET /entities?entity_type=connectionreturns REST Messages- NHI count drops from ~92 to ~8-15 (accurate)
- Graph shows distinct node shapes/colors per entity type
- All existing tests pass with updated types
- Architecture docs (02-data-model, 01-database, 04-connectors, glossary) updated
After Workstream B Phase 1 (Execution Chains)
GET /execution-chainsreturns all discovered chains- Each chain has stable ID (survives entity rotation)
- Chain composition_hash detects structural changes between scans
- Chains include OAA canonical permission summary
- UI: Execution Chains list page with filter/sort
- UI: Chain detail page with entity list + summary + graph
After Workstream B Phase 2 (Temporal Chains)
- Chain version history stored on each structural/summary change
GET /execution-chains/:id/diffcompares two timestamps- Chain events emitted (created, entity_added, blast_radius_changed)
- UI: "Was → Is" temporal comparison tab on chain detail page
After Workstream D (UI/Connector Fixes)
- "Show full chain" toggle on automation detail pages
- Flow diagram shows correct trigger for Script Includes
- 0 "unknown" execution_mode flows
- setWorkflow(false) shows warning badge
- Data domain badges visible on graph nodes
8. ADRs to Create
| ADR | Decision | When | Supersedes |
|---|---|---|---|
| adr-006-entity-type-reclassification.md | Split autonomous_identity into identity + automation + connection + credential. NormalizedNodeType keeps human_identity; platform stores owner. | WA Phase A0 (before code) | Replaces planned adr-006 (subtype taxonomy) |
| adr-007-execution-relationship-types.md | CALLS + INVOKES + USES + AUTHENTICATES_AS edge types. Edge migration mapping from old types. | WA Phase A0 (before code) | Extends original CALLS-only ADR |
| adr-008-execution-chains-collection.md | New execution_chains collection for chain persistence | WB start | New |
| adr-009-oaa-export-projection.md | OAA as export format, not internal model | When WC starts | New |
9. Risk Assessment
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| Path materializer rework takes longer than 14h | Medium | Medium | It's the hardest single task. Budget 2 extra days. Existing path-materializer tests serve as regression suite. |
| Entity reclassification breaks unexpected code paths | Medium | Medium | Phase A0 compatibility layer means old data keeps working. Run full test suite after each phase. No clients to break. |
| Edge migration leaves orphan edges in DB | Low | Low | No existing data (no clients). Fresh ingest with new connector. |
| Chain builder BFS performance on large graphs | Low | Medium | Complexity budget (50-node cap per chain). Index on entity_type. |
| Phase 2 temporal tracking deferred too long | Medium | Medium | Explicit milestone M5 at Week 7. Product feedback makes this a P1. |
| OAA export deferred too long, blocks sales | Low | High | C1-C2 can ship in 2 days if urgently needed. |
| Integrator's dissent is correct (BRs should stay identity) | Low | Low | Types are easy to rename later. No clients. |
| Connector contract drift during migration window | Low | Medium | Dual-accept ensures both old and new connector versions work. Migration window is ~1 week (Phase A0→A2). |
10. Plan Review Findings (Incorporated)
This plan was updated to address 8 review findings:
| # | Finding | Severity | Resolution |
|---|---|---|---|
| 1 | WA under-scoped (52h vs 168h developer audit) | Critical | Re-baselined to ~106h. 13 identity-assumption locations across 47 files. Phased execution with rollback points. |
| 2 | New vocabulary breaks ingestion validators | Critical | Phase A0 adds compatibility layer: dual-accept old+new types with ingestion normalizer. |
| 3 | owner in NormalizedNodeType conflicts with connector contract | Critical | Fixed: NormalizedNodeType keeps human_identity. Platform stores owner internally. Normalizer maps. |
| 4 | Identity-only assumptions in 5+ locations | High | Explicit file-by-file audit in A.5. Path materializer re-estimated at 12-16h. |
| 5 | Edge migration mapping missing | High | Added A.3.1 migration table: old→new edge mapping with compatibility window. |
| 6 | Chain temporal tracking missing from plan | High | Added B.6 Phase 2: execution_chain_versions + diff API + events + UI (~40h). |
| 7 | RUNS_AS typing unresolved for flow→human user | Medium | Fixed: RUNS_AS constraint is automation → identity | owner. |
| 8 | Doc/ADR updates missing from WA tasks | Medium | Added A0.1 (ADRs before code) and A4.3 (doc updates). Sequenced with implementation. |
11. Analysis References
| Plan Section | Analysis Source |
|---|---|
| Workstream A: Entity types | Round 5 Synthesis |
| Workstream A: Decision tree | Round 5 Architect |
| Workstream A: File impact audit | Round 5 Developer |
| Workstream B: execution_chains | Round 3 Synthesis |
| Workstream B: Chain builder | Round 3 Architect |
| Workstream B: Effort estimates | Round 3 Developer |
| Workstream C: OAA export | Round 4 Synthesis |
| Workstream C: Execute gap | Round 4 CISO |
| Workstream D: UI fixes | Round 2 Synthesis |
| Security relevance tiers | Round 1 Synthesis |