W1 Unified Implementation Plan
Date: 2026-02-17 Status: Ready for implementation — all documentation updates complete (Phase 6b done), all 5 mockups analyzed, 3 review rounds passed Scope: Workload rename + W1 product requirements across platform, connectors, UI, and documentation Architecture docs: Updated in parallel — see 01-data-model.md (W1 Derived Concepts section), 03-database.md, ADR-010
Executive Summary
This plan consolidates two major work streams into a single phased execution:
- Workload Rename —
entity_type: "automation"→"workload"across ~45 files (detailed in doc 11) - W1 Product Requirements — New evaluator rules, risk clusters, exposure concept, posture summary, UX redesign
Key finding from 5-agent analysis: The data layer is almost entirely in place. The connector already produces execution_mode, egress_category, ownership_status, risk_group, data_domains, execution_count_30d, and security_relevance on automation entities. The gap is in:
- Evaluator rules that surface risk from data that already exists (5-6 new rules)
- Aggregation APIs for posture summary and risk clusters
- UX reshaping to match W1 vocabulary and investigation flow
Estimated total effort: 8-10 working days for a demo-able W1.
1. Current State Assessment
What's DONE (no backend work needed)
| W1 Capability | Current Implementation |
|---|---|
| Autonomous execution inventory | Connector classifies execution_mode (autonomous/operator_assisted/human_triggered), platform stores, UI shows filterable table |
| Identity binding resolution | RUNS_AS relationships, BindingStatusBadge, ExecutionFlowDiagram shows identity chain |
| Data reachability classification | chain-builder.ts BFS traversal, execution_paths[] on entities, blast-radius API, UI Effects tab |
| Egress classification | egress_classifier.py in connector, egress_category/egress_host properties, EgressBadge throughout UI |
| Ownership validation | ownership_validator.py in connector, orphaned_ownership/ownership_degraded evaluator rules, OwnershipBadge in UI |
| Evidence completeness | 6 categories (current_roles, role_history, execution_evidence, ownership_records, approval_records, credential_state), EvidenceCompletenessBar component |
| Authority path diagram | ExecutionFlowDiagram.tsx renders linear flow: Trigger → Entry Point → Calls → Runs As → Can Access |
| Standing authority data | execution_mode, auth_protocol from cross-system auth, human_session_required derivable |
What's PARTIAL (reshape/extend)
| W1 Capability | Gap |
|---|---|
| Posture summary | Data exists on entity properties, needs aggregation API endpoint |
| Risk clusters | Connector produces RG1-RG5 (2D), W1 needs 4D compound clusters (+ execution status + ownership) |
| Exposures concept | Automations page is ~80% of Exposure View; needs rename, regroup, inline expand |
| Homepage | Dashboard has similar ingredients but wrong shape; needs execution visibility categories |
| Execution validation | dormant_authority checks evidence age, W1 needs binary proven/unproven classification |
What's NEW (build from scratch)
| W1 Capability | Description |
|---|---|
| 5-6 new evaluator rules | unproven_execution, unknown_identity_binding, reachable_sensitive_domain, llm_egress, external_egress, ownership_unknown |
| Risk cluster aggregation API | GET /api/v1/posture/risk-clusters — compound condition grouping |
| Posture summary API | GET /api/v1/posture/summary — 4 execution visibility categories |
| Delta since last refresh | +X new autonomous identities, +Y ownership invalidations |
| Dark sidebar navigation | Complete UX overhaul: 8 items → 7 items, white → dark navy (Graph Explorer retained) |
| Expandable exposure rows | Inline detail panel with authority path, standing authority, ownership breakdown, evidence completeness |
2. Architecture Decisions
Decision 1: Exposures = Entity + Finding Projections (NOT execution chains, NOT a new collection)
An Exposure is a derived assessment unit representing one Authority Path (Workload → Identity → Destination → Data Domain) enriched with its associated findings, execution activity, ownership state, and evidence completeness. Key distinctions:
- Exposure ≠ Execution Chain. One
ExecutionChainDocis a BFS tree that can span multiple branches and resources. One Exposure is a single linear path through the entity graph. - Exposure ≠ Finding. A finding is an issue-level alert. An Exposure is the workload-level authority summary to which findings are attached.
- Not persisted. Exposures are computed from entity relationships and
execution_paths[]at query time. - Not dependent on
execution_chainscollection. W1 scope explicitly excludes chain persistence (logic.md section 9). Exposures are computed from entities directly.
Computation: For each workload entity, attempt to resolve its RUNS_AS identity. Two paths:
- Identity resolved: Collect
execution_paths[]from the identity, group by (destination, data_domain) to produce one Exposure per unique authority path. Enrich with finding counts, execution stats, and ownership state. - Identity unknown (no RUNS_AS, or non-unique target): Per logic.md section 2, unknown is a first-class result. Produce an Exposure with
identity=unknown,destination=unknown,data_domain=unknown. The exposure still appears in the list with itsunknown_identity_bindingfinding attached. This ensures every autonomous workload has at least one Exposure row — unknown states are visible, not silently dropped.
A new /api/v1/exposures endpoint serves this data. The existing /api/v1/execution-chains endpoint is NOT reused for W1.
Decision 2: Risk Clusters = Computed Aggregations (NOT persisted)
Per W1 logic.md section 7: "Grouping does not replace canonical findings and does not introduce new risk semantics." Risk Clusters are ephemeral, deterministic aggregations computed from existing entity properties:
egress_category(llm/external/internal)execution_count_30d(>0 = active, 0 = dormant)ownership_status(valid/invalid/ambiguous/unknown)max_sensitivityfromexecution_paths[]
Computed via MongoDB aggregation pipeline. No new collection needed.
Decision 3: Workload Rename Before W1 Rules
The workload rename (entity_type: "automation" → "workload") should happen BEFORE writing new W1 evaluator rules. This avoids writing new rules against the old name and immediately renaming them.
Decision 4: W1 Finding Types Coexist with Existing Rules
Existing evaluator rules (scope_drift, privilege_justification_gap, unresolved_cross_system_auth) are W2+ scope per product docs. They should continue to exist and run, but W1 views filter to W1 finding types only. No rules are deleted.
3. W1 Finding Type Mapping
| W1 Finding Type | Status | Implementation |
|---|---|---|
unproven_execution | NEW_RULE | Fires when a workload can execute autonomously but no execution evidence can be deterministically linked to it or its RUNS_AS identity. This includes: (a) zero evidence records, AND (b) evidence exists but deterministic linkage fails (per logic.md section 1: "condition (4) cannot be satisfied"). Binary: proven or unproven. dormant_authority keeps handling stale evidence (>90 days) separately. |
unknown_identity_binding | NEW_RULE | Fires when a workload has no deterministic RUNS_AS relationship, OR the RUNS_AS target identity is not uniquely identifiable (per logic.md section 2: "identity object is uniquely identifiable"). Broader than unresolved_cross_system_auth (which only checks oauth_app + binding_status). No heuristic correlation permitted. |
reachable_sensitive_domain | NEW_RULE | Fires when entity has execution_paths with sensitivity in ("confidential", "restricted"). Structural exposure signal — always fires when the path exists. Severity: medium (confidential), high (restricted). |
llm_egress | NEW_RULE | Fires when properties.egress_category === "llm". Trivial property check. Severity: high. |
external_egress | NEW_RULE | Fires when properties.egress_category === "external". Same pattern. Severity: medium. |
ownership_invalid | EXISTS | Maps to current orphaned_ownership. Alias at API layer or rename (decision: alias to avoid breaking finding IDs). |
ownership_ambiguous | NEW_RULE | Distinct from ownership_degraded. Fires when the entity has ONLY group/team owners and has never had an individual owner assigned (per 01-data-model.md ownership rules). ownership_degraded = individual was once assigned and lost; ownership_ambiguous = no individual was ever assigned. |
ownership_unknown | NEW_RULE | Fires when insufficient deterministic metadata to determine ownership. Different from orphaned (no OWNED_BY edges) — this is metadata quality issue. |
4. New API Endpoints
GET /api/v1/posture/summary
Returns all 4 posture stat cards in one response:
{
"data": {
"active_autonomous": { "count": 142, "label": "Execution Identities" },
"dormant_authority": { "count": 38, "label": "Dormant Authority Identities" },
"operator_assisted": { "count": 856, "label": "Assisted Workloads" },
"human_triggered": { "count": 2100, "label": "Human-Triggered Workloads" }
},
"delta_since_last_refresh": {
"new_autonomous_identities": 2,
"ownership_invalidations": 3,
"last_sync_at": "2026-02-16T10:42:00Z"
}
}
Implementation: MongoDB aggregation pipeline with identity-first counting for cards 1-2. The pipeline must count distinct identities (RUNS_AS targets of autonomous workloads), not raw workload entities. Multiple workloads sharing one identity count as 1.
- Cards 1-2 (Execution Identities, Dormant Authority): Count distinct
identityentities that are RUNS_AS targets, split byexecution_count_30d > 0vs== 0. - Cards 3-4 (Assisted Workloads, Human-Triggered Workloads): Count
workloadentities byexecution_mode. UI labels use "Workloads" (not "Automations") per terminology decision.
GET /api/v1/posture/risk-clusters?limit=10
Returns top risk clusters:
{
"data": [
{
"cluster_key": "sensitive:llm:active:ownership_invalid",
"label": "Sensitive + LLM + Active + Invalid Owner",
"identity_count": 12,
"execution_count_30d": 8400,
"sensitive_domains": ["financial", "identity"],
"ownership_invalid_count": 12,
"priority": "P0"
}
]
}
Implementation: MongoDB aggregation with two stages — first group workloads by compound key (max_sensitivity_bucket, egress_category, execution_active, ownership_status), then deduplicate RUNS_AS identity targets for the identity_count metric. The identity_count field must reflect distinct identities, not workloads.
GET /api/v1/exposures?cluster_key=&limit=50
New endpoint (NOT a modification of /api/v1/execution-chains). Computes exposures from entity graph:
- Query workload entities (with optional filters:
cluster_key,execution_mode,finding_type,data_domain) - For each workload, attempt to resolve RUNS_AS identity target
- If identity resolved: Collect
execution_paths[]from the identity, group by (destination, data_domain). Produce one Exposure per unique authority path tuple: (workload_id, identity_id, destination, data_domain) - If identity unknown (no RUNS_AS or non-unique target): Produce a single Exposure with
identity=unknown,destination=unknown,data_domain=unknown. Ensures every autonomous workload appears in the exposure list — unknown is a first-class state, not silently dropped. - Enrich each exposure with:
execution_count_30d,last_execution_at,ownership_status,data_domains,finding_count,finding_types[],evidence_completeness
The exposure ID is deterministic: EXP-{short_hash(tenant_id, workload_id, identity_id, destination, data_domain)}.
GET /api/v1/exposures/:id (Exposure Detail)
Returns a single exposure with full detail for 6 panels (per mockup exposure--graph.jpg):
{
"data": {
"exposure_id": "EXP-021",
"title": "Auto-GPT Instance #42 → OpenAI",
"evidence_verified_at": "2026-02-16T10:42:00Z",
"authority_path": {
"workload": { "id": "...", "name": "Auto-GPT Instance #42", "entity_type": "workload" },
"identity": { "id": "...", "name": "svc_llm_processor", "entity_type": "identity", "subtype": "service_principal" },
"destination": { "id": "...", "name": "OpenAI API Gateway", "entity_type": "connection" },
"data_domain": { "id": "...", "name": "Financial", "sensitivity": "confidential" },
"edges": [
{ "source": "workload", "target": "identity", "type": "RUNS_AS" },
{ "source": "workload", "target": "destination", "type": "INVOKES" },
{ "source": "destination", "target": "data_domain", "type": "REACHES", "derived": true }
]
},
"standing_authority": {
"execution_model": "autonomous",
"auth_type": "client_credentials",
"human_session_required": false
},
"deterministic_linkage_proof": {
"issuing_tenant": "acme-prod",
"target_instance": "openai-org-772",
"match_type": "deterministic",
"match_detail": "Matching sub_id value workload-42-primary"
},
"ownership_breakdown": {
"primary": { "status": "departed", "owner_name": null },
"secondary": { "status": "none" },
"inherited": { "status": "present", "owner_name": "Platform Team" }
},
"workload_metadata": {
"source_system": "github",
"artifact_identifier": "auto-gpt-main",
"last_refreshed_at": "2026-02-16T10:40:00Z"
},
"identity_binding": {
"relationship": "RUNS_AS",
"label": "Workload Identity",
"protocol": "OIDC (Federated)",
"target_system": "gcp"
},
"findings": [...],
"evidence_completeness": {...},
"execution_evidence_summary": {
"total_records": 1420,
"last_execution_at": "2026-02-16T10:40:00Z",
"execution_count_30d": 1420
}
}
}
Key data sources:
- Standing authority:
execution_modefrom entity properties,auth_typefrom cross-system auth record,human_session_requiredderived from execution_mode - Deterministic linkage proof: Cross-system auth record fields (
issuing_tenant,target_instance,binding_status, matching criteria) - Ownership breakdown: OWNED_BY relationships resolved to owner entities with
statusfield - Workload metadata: Entity
source_system,source_id,updated_at - Identity binding: RUNS_AS relationship type + cross-system auth
protocol+ identity entity'ssource_system - Execution evidence: Aggregated from
execution_evidenceentities linked to the workload or its RUNS_AS identity (E3 cross-lookup)
5. UX Redesign
5.0 Mockup Reference
All mockups are in docs/product/wedges/w1-exposure/mockups/:
| Mockup | File | Shows |
|---|---|---|
| Exposure Discovery | 2026-02-17-mockup-exposure-discovery.jpg | W1 landing page: Posture Summary (4 cards), delta indicators, Top 5 Risk Clusters, collapsed findings view |
| Exposures List | 2026-02-17-mockup-expsosures-list.jpg | Exposure table with search, filters, pagination, data domain badges, egress category. "Create Ticket" + "Export" buttons |
| Risk Cluster Drill-down | 2026-02-17-mockup-risk-clusters.jpg | Cluster → exposures list with inline expandable rows showing authority path, standing authority, ownership, evidence completeness |
| Exposure Detail (Graph) | 2026-02-17-mockup-exposure--graph.jpg | Single exposure detail: interactive authority path graph, 6 detail panels (Standing Authority, Deterministic Linkage Proof, Ownership Breakdown, Workload Metadata, Identity Binding, Execution Evidence), "Create Ticket" button |
| Relationship Diagram | 2026-02-17-relationship-diagram.jpg | Conceptual model: Automation Definition → Authority Path → Exposure ↔ Risk Cluster; Authority Path → Automation Run → Execution Evidence |
5.0.1 Cross-Mockup Consistency Notes
Minor mockup discrepancies (resolve during implementation):
- Nav item count: Discovery mockup shows 5 nav items (missing "Exposures"). Exposures List and Detail mockups show 6 items. Canonical: 7 items (Overview, Clusters, Exposures, Identities, Data Domains, Graph Explorer, Settings). Graph Explorer is retained from current UI — it's already built and provides value for investigation.
- Nav label "Clusters" vs "Risk Clusters": Discovery mockup uses "Clusters", Risk Cluster drill-down mockup uses "Risk Clusters". Canonical: "Clusters" (shorter, consistent with Discovery mockup).
- Identity node type in detail graph: Exposure Detail mockup labels the identity node as
entity_type: "resource"with a "service" badge. In the real data model, RUNS_AS targets areentity_type: "identity". Implementation: use actual entity_type from data, the mockup label is illustrative. - User personas differ across mockups: "Sarah Jenkins, SecOps Admin" (Discovery), "Jane Doe, Security Analyst" (List, Detail). This is intentional (different user roles), not an inconsistency.
5.1 Navigation Restructure
| Current (8 items, white sidebar) | W1 (7 items, dark navy sidebar) |
|---|---|
| Dashboard | Overview ("Exposure Discovery") |
| Automations | Exposures (formerly Automations) |
| Findings | (moved under Exposure detail) |
| Entities | Identities (filtered to workloads + identities) |
| Graph Explorer | Graph Explorer (retained — already built, useful for investigation) |
| Chains | (replaced by Exposures) |
| Syncs | (moved to Settings) |
| Temporal Compare | (available via link from entity pages) |
| — | Clusters (new) |
| — | Data Domains (new) |
| — | Settings (new, contains Syncs + config) |
5.2 Page Mapping
| W1 Page | Source | Reuse % | Key Changes |
|---|---|---|---|
| Exposure Discovery (Homepage) | Dashboard.tsx | 60% | Title "Exposure Discovery". Replace stat cards with 4 Posture Summary cards, add delta indicators, add Top 5 Risk Cluster cards, add collapsed Filtered Findings View at bottom |
| Exposures List | AutomationsPage.tsx | 80% | Rename, add search by Exposure ID/name, add Filters button, add pagination ("Showing 1-12 of 145 exposures"), add "Create Ticket" + "Export" buttons. Table columns: Exposure ID, Exposure (name with → arrow notation), Last Execution, Executions (30D), Ownership Status, Data Domains, Egress Category |
| Exposure Detail | AutomationDetailPage.tsx | 50% | Major rework — see section 5.6 below. Interactive authority path graph, 6 detail panels, "Create Ticket" button, breadcrumb navigation |
| Risk Cluster Drill-down | NEW (but reuses Exposures table pattern) | 40% | Breadcrumb: "Risk Clusters > [cluster name]". Exposures table filtered by cluster, with inline expandable rows showing authority path + standing authority + ownership + evidence completeness. "Export" + "Create ticket" buttons |
| Identities | EntitiesListPage.tsx | 85% | Filter to workloads + identities only |
| Data Domains | NEW | 0% | New page browsing resources by business domain |
| Settings | NEW | 30% | Move Syncs, tenant config, user settings here |
5.3 New Components Required
RiskClusterCard— Compound condition title (e.g., "Sensitive + LLM + Active + Invalid Owner"), "COMPOUND CONDITION" label, data domain badges with icons, dual metrics (Identities + 30d Execs), ownership warning pill (red), priority border color (red = highest, blue = others)DataDomainBadge— Pill badges with icons for FIN, GenAI, PII, SRC, SaaS, EXT, INT, Logs, Keys, Customer Data, EngDeltaIndicator— Change-since-refresh inline badges: "+2 new autonomous identities", "+3 ownership invalidations", "Since Last Refresh"ExpandableExposureRow— Row with chevron toggle and inline detail panel showing: authority path (4-node linear with icons), standing authority, ownership breakdown, evidence completeness. Footer: "Evidence verified: X mins ago" + "Cross-system identity linkage verified" status + "View Full Detail →" linkStandingAuthorityPanel— Key-value card: Execution Model (Autonomous/Assisted/Human), Auth Type (Client Credentials/etc.), Human Session Required (Yes/No)OwnershipBreakdownPanel— Per-owner status: Primary (Departed/Present/None), Secondary (Departed/Present/None), Inherited (Departed/Present/None) with status iconsDeterministicLinkageProofPanel— Cross-system linkage evidence: Issuing Tenant, Target Instance, "Deterministic Match" badge with matching detail (e.g., "Matching sub_id value workload-42-primary"). Shows how the cross-system identity join was established deterministically.WorkloadMetadataPanel— Source System (with platform icon), Artifact Identifier, Last Refreshed timestampIdentityBindingPanel— Relationship type (RUNS_AS + "Workload Identity" label), Protocol (OIDC Federated/SAML/etc.), Target System (with platform icon: GCP/Azure/AWS)Breadcrumb— Navigation breadcrumb component (e.g., "Exposures > EXP-021" or "Risk Clusters > Sensitive + LLM + Active + Invalid Owner")UserProfileHeader— Top-right user info with avatar, name, role, orgRefreshTimestamp— Pill with clock icon showing "Last Refreshed: Feb 16, 2026, 10:42 AM"CreateTicketButton— "Create Ticket" action button, appears on Exposures List, Exposure Detail, and Risk Cluster drill-downExportButton— "Export" action button with download iconFilteredFindingsView— Collapsible findings table for the Overview page, pre-filtered to W1 finding types. Shown collapsed by default with "Filtered Findings View (Collapsed)" label.
5.4 Existing Components Reusable
| Component | Reuse Strategy |
|---|---|
StatCard.tsx | Extend with accentColor, statusDot, subtitle props for Posture Summary cards |
ExecutionFlowDiagram.tsx | Two rendering modes (both linear per ux.md): (1) Inline expand: compact icon-based 4-node path, (2) Detail page: full 4-node linear diagram with entity-type colored cards and labeled edges (RUNS_AS, INVOKES, REACHES — note: REACHES is a display label, not a stored relationship) |
AutomationBadges.tsx | Rename to WorkloadBadges.tsx (part of workload rename), extend for new badge types |
EvidenceCompletenessBar.tsx | Reuse as-is: 6-category horizontal bars in both inline expand and detail page |
DataTable.tsx | Already supports renderSubRow and expandedRowId — use existing expand pattern, no extension needed |
SeverityBadge.tsx | Reuse for finding severity in exposure rows |
OwnershipBadge.tsx | Reuse for ownership status column in exposure table (Valid/Invalid badges) |
5.5 Exposure Discovery Page (Overview) Detail
Per mockup: 2026-02-17-mockup-exposure-discovery.jpg
Layout (top to bottom):
- Header: "Exposure Discovery" title +
RefreshTimestamppill +UserProfileHeader - Posture Summary: 4
StatCardcards in a row:- Card 1: "EXECUTION IDENTITIES" = 142, red dot, "Autonomous Authority" — counts distinct RUNS_AS identity targets
- Card 2: "DORMANT AUTHORITY IDENTITIES" = 38, orange dot, "No execution in Last 30d" — counts distinct identities with zero 30d executions
- Card 3: "ASSISTED WORKLOADS" = 856, blue dot, "Requires Human Session" — counts workloads with
execution_mode: "operator_assisted" - Card 4: "HUMAN-TRIGGERED WORKLOADS" = 2.1k, grey dot, "User-Initiated" — counts workloads with
execution_mode: "human_triggered"
- Delta indicators:
DeltaIndicatorbadges: "+2 new autonomous identities", "+3 ownership invalidations", "Since Last Refresh" - Top 5 Autonomous Authority Risk Clusters: Section title + "View all clusters →" link. 5
RiskClusterCardin a row, each showing:- Priority border (red = P0, blue = others)
- Compound condition title
- "COMPOUND CONDITION" label
- Data domain badges (FIN, GenAI, PII, SRC, SaaS, EXT, INT)
- Egress type badge if applicable
- Dual metrics: "Identities" count + "30d Execs" count
- Ownership warning pill (red) or neutral status text ("All ownership verified", "No status flags")
- Filtered Findings View: Collapsible section at bottom, collapsed by default. When expanded, shows a findings table filtered to W1 finding types.
5.6 Exposure Detail Page Detail
Per mockup: 2026-02-17-mockup-exposure--graph.jpg
Layout (top to bottom):
- Breadcrumb: "Exposures > EXP-021"
- Header: "EXP-021 | Auto-GPT Instance #42 → OpenAI" +
CreateTicketButton - Subheader: "Evidence verified: Feb 16, 2026, 10:42 AM"
- Authority Path Graph: 4-node linear diagram (per ux.md section 3A: "Linear only"). Nodes have entity-type styling:
- Workload node: blue dashed border, entity name + "workload" type label
- Identity node: coral/red background, entity name + subtype badge (e.g., "service_principal")
- Destination node: cyan border, entity name + "connection" type label
- Data Domain node: data domain badge with sensitivity level
- Edge labels: "RUNS_AS" (workload→identity), "INVOKES" (workload→destination), "REACHES" (destination→data domain — display label only, not a stored relationship; derived from
execution_paths[]) - Note: The mockup shows 3 visible nodes with data domain as a badge on the destination. Implementation adds data domain as a true 4th node for consistency with the 4-node authority path model. Per user decision: more detail is better.
- Detail cards (2-column grid, 3 rows):
- Standing Authority (left): Execution Model, Auth Type, Human Session Required
- Ownership Breakdown (right): Primary/Secondary/Inherited owner with Departed/Present/None status
- Deterministic Linkage Proof (left): Issuing Tenant, Target Instance, "Deterministic Match" with sub_id matching evidence
- Workload Metadata (right): Source System (with icon), Artifact Identifier, Last Refreshed
- (bottom left: empty or additional space)
- Identity Binding (right): RUNS_AS relationship, Protocol (OIDC Federated), Target System (GCP)
- Execution Evidence (bottom): Section showing linked execution evidence records
6. Phased Execution Plan
Phase 0: Workload Rename (Day 1-2)
Detailed in doc 11 — workload-rename-implementation-plan.md
Execute the full workload rename FIRST so all subsequent work uses the new vocabulary:
- Platform core:
entity_type: "workload",WORKLOAD_SUBTYPES,workloadSubtype - Platform tests: Update all fixtures and assertions
- UI:
"workload"in types, color maps, filters; rename files - Connector:
node_type="workload",workloadSubtype - DB migration:
entities,entity_versions,execution_chains - Backward compat: Accept
"automation"as deprecated alias
Verification: npm test + npx tsc --noEmit + cd ui && npm run build + pytest
Phase 1: W1 Domain Types + Evaluator Rules (Day 2-3)
1a. Add W1 finding types to type system
| File | Changes |
|---|---|
src/domain/findings/types.ts | Add to FINDING_TYPES: unproven_execution, unknown_identity_binding, reachable_sensitive_domain, llm_egress, external_egress, ownership_ambiguous, ownership_unknown |
ui/src/api/api-types.ts | Add same types to FindingType union |
1b. Write new evaluator rules
| File | Rule | Logic |
|---|---|---|
src/evaluator/rules/unproven-execution.ts (NEW) | unproven_execution | Workload can execute autonomously but no ExecutionEvidence can be deterministically linked to it or its RUNS_AS targets. Covers both zero records AND records that exist but lack deterministic linkage. |
src/evaluator/rules/unknown-identity-binding.ts (NEW) | unknown_identity_binding | Workload has no RUNS_AS relationship, OR the RUNS_AS target identity is not uniquely identifiable. No heuristic correlation. |
src/evaluator/rules/reachable-sensitive-domain.ts (NEW) | reachable_sensitive_domain | Entity has execution_paths with sensitivity in (confidential, restricted) |
src/evaluator/rules/egress-exposure.ts (NEW) | llm_egress, external_egress | Check properties.egress_category === "llm" or === "external" |
src/evaluator/rules/ownership-gaps.ts (NEW) | ownership_ambiguous, ownership_unknown | Group-only ownership / insufficient metadata |
src/evaluator/rules/dormant-authority.ts (MODIFY) | Split | Add guard: if zero evidence records, return null (let unproven_execution handle it). Only fire when evidence exists but is stale (>90 days). |
src/evaluator/rules/index.ts (MODIFY) | Register | Add all new rules to ALL_RULES array |
1c. Tests for new rules
- Unit tests for each new rule (~2 tests each minimum)
- Update
dormant-authority.test.tsfor split behavior
Verification: npm test — all tests pass
Phase 2: Aggregation APIs (Day 3-4)
2a. Storage layer — add aggregation support
| File | Changes |
|---|---|
src/storage/storage-adapter.ts | Add aggregatePostureSummary(tenantId) and aggregateRiskClusters(tenantId, limit) to interface |
src/storage/mongo/adapter.ts | Implement using MongoDB aggregation pipeline ($match, $group, $sort) |
2b. New API routes
| File | Endpoints |
|---|---|
src/api/routes/posture.ts (NEW) | GET /api/v1/posture/summary — 4 execution visibility counts + delta |
src/api/routes/posture.ts (NEW) | GET /api/v1/posture/risk-clusters — compound condition aggregation |
src/api/routes/exposures.ts (NEW) | GET /api/v1/exposures + GET /api/v1/exposures/:id — compute from entity graph, NOT from execution_chains |
2c. Exposure computation logic
| File | Changes |
|---|---|
src/domain/exposures/compute.ts (NEW) | Pure function: given a workload entity + its RUNS_AS identity + identity's execution_paths + associated findings → produce Exposure view models. No dependency on execution_chains collection or chain-builder.ts. |
2d. Route registration
| File | Changes |
|---|---|
src/api/app.ts | Register createPostureRoutes(deps.storageAdapter) and createExposureRoutes(deps.storageAdapter) alongside existing route mounts (lines 66-74). Routes mount in app.ts, not index.ts. |
Verification: npm test + npm run test:integration + manual API smoke test
Phase 3: UI Workload Rename + Foundation (Day 4-5)
Included in Phase 0 workload rename, but listing UI-specific changes for clarity:
| File | Changes |
|---|---|
ui/src/api/api-types.ts | "automation" → "workload" in EntityType union |
ui/src/hooks/use-automations.ts → use-workloads.ts | Rename hook, entity_type: "workload" |
ui/src/components/AutomationBadges.tsx → WorkloadBadges.tsx | Rename exports |
All UI files referencing "automation" | Update to "workload" |
Phase 4: W1 UX Redesign (Day 5-8)
4a. Navigation + Layout
| File | Changes |
|---|---|
ui/src/components/Layout.tsx | Dark navy sidebar, new nav items (Overview, Clusters, Exposures, Identities, Data Domains, Graph Explorer, Settings), SecurityV0 branding with Admin Console subtitle, UserProfileHeader top-right |
ui/src/App.tsx | New routes: /, /clusters, /exposures, /identities, /data-domains, /graph, /settings; redirect /automations/* → /exposures/*; keep existing graph explorer route |
4b. Exposure Discovery (Homepage) — per mockup 2026-02-17-mockup-exposure-discovery.jpg
| Component | Source | Changes |
|---|---|---|
| Page rename | DashboardPage.tsx → ExposureDiscoveryPage.tsx | Title "Exposure Discovery", completely new layout per section 5.5 |
PostureSummaryCards | Extend StatCard.tsx | 4 cards with colored accent dot, subtitle. New usePostureSummary hook calling /api/v1/posture/summary |
DeltaIndicator | NEW | "+2 new autonomous identities", "+3 ownership invalidations", "Since Last Refresh" |
RiskClusterCards | NEW | Top 5 clusters with priority borders. New useRiskClusters hook calling /api/v1/posture/risk-clusters. "View all clusters →" link |
RefreshTimestamp | NEW | Clock icon + last sync timestamp pill in header |
FilteredFindingsView | NEW | Collapsible W1 findings table at bottom, collapsed by default |
4c. Exposures Page (replaces Automations) — per mockup 2026-02-17-mockup-expsosures-list.jpg
| Component | Source | Changes |
|---|---|---|
| Page rename | AutomationsPage.tsx → ExposuresPage.tsx | Title "Exposures". Search bar ("Search by Exposure ID or name..."), Filters button, pagination ("Showing 1-12 of 145 exposures"), CreateTicketButton + ExportButton in header |
| Table columns | Reframe from automation columns | EXPOSURE ID (EXP-NNN, clickable link), EXPOSURE (name with → arrow notation + execution mode badge), LAST EXECUTION, EXECUTIONS (30D), OWNERSHIP STATUS (Valid/Invalid badge), DATA DOMAINS (pill badges), EGRESS CATEGORY (text with color: LLM=orange, External=red, Internal=grey) |
| Expandable rows | DataTable.tsx (already supports renderSubRow + expandedRowId) | Implement renderSubRow callback; no DataTable extension needed |
4d. Exposure Detail (replaces Automation Detail) — per mockup 2026-02-17-mockup-exposure--graph.jpg
| Component | Source | Changes |
|---|---|---|
| Page rename | AutomationDetailPage.tsx → ExposureDetailPage.tsx | Major rework per section 5.6. Breadcrumb, interactive authority path graph, 6 detail panels, Create Ticket button |
AuthorityPathGraph | Adapt ExecutionFlowDiagram.tsx | 4-node linear diagram (per ux.md: "Linear only") with entity-type colored nodes, labeled edges (RUNS_AS, INVOKES, REACHES). Data domain rendered as true 4th node. |
StandingAuthorityPanel | NEW | Execution Model / Auth Type / Human Session Required |
DeterministicLinkageProofPanel | NEW | Issuing Tenant, Target Instance, Deterministic Match badge with sub_id evidence |
OwnershipBreakdownPanel | NEW | Primary/Secondary/Inherited owner with Departed/Present/None status |
WorkloadMetadataPanel | NEW | Source System (icon), Artifact Identifier, Last Refreshed |
IdentityBindingPanel | NEW | RUNS_AS relationship, Protocol (OIDC/SAML), Target System (GCP/Azure/AWS icon) |
| Execution Evidence section | NEW or extend existing evidence tab | Linked execution evidence records for the workload and its RUNS_AS identity |
4e. Risk Cluster Drill-down — per mockup 2026-02-17-mockup-risk-clusters.jpg
| Component | Source | Changes |
|---|---|---|
RiskClusterDrilldownPage.tsx | NEW (but reuses Exposures table) | Breadcrumb: "Risk Clusters > [cluster name]". Title "Exposures: [cluster name]". ExportButton + CreateTicketButton. Exposures table filtered by cluster_key. Inline expandable rows with full detail panel. |
ExpandableExposureRow detail panel | NEW | 3-column layout: Authority path (4-node linear with icons), Standing Authority, Ownership Breakdown, Evidence Completeness (6 bars). Footer: "Evidence verified: X ago" + "Cross-system identity linkage verified" + "View Full Detail →" link |
RiskClustersPage.tsx (grid view) | NEW | Grid of RiskClusterCard components, click to drill into RiskClusterDrilldownPage |
RiskClusterCard | NEW | Priority border (red/blue), compound condition title, "COMPOUND CONDITION" label, domain badges, identity count, 30d execs, ownership warning |
DataDomainBadge | NEW | Icon + label pills for FIN, GenAI, PII, SRC, SaaS, EXT, INT, Logs, Keys, Customer Data, Eng |
Phase 5: Connector Workload Rename (Day 8-9)
From workload rename plan:
| File | Changes |
|---|---|
transformer.py | node_type="workload", workloadSubtype, _enrich_workload_properties |
correlator.py | Variable names, comments |
tests/unit/test_transformer.py | Property name assertions |
| Other connector files | Comments/docstrings |
Verification: pytest — all tests pass
Phase 6: Database Migration + Documentation (Day 9-10)
6a. Database migration
// Run AFTER deploying code that accepts both old and new values
db.entities.updateMany(
{ entity_type: "automation" },
[{ $set: {
entity_type: "workload",
"properties.workloadSubtype": "$properties.automationSubtype"
}}]
);
db.entity_versions.updateMany(
{ entity_type: "automation" },
[{ $set: {
entity_type: "workload",
"properties.workloadSubtype": "$properties.automationSubtype"
}}]
);
db.execution_chains.updateMany(
{ "entity_refs.entity_type": "automation" },
{ $set: { "entity_refs.$[elem].entity_type": "workload" } },
{ arrayFilters: [{ "elem.entity_type": "automation" }] }
);
6b. Documentation updates
| File | Status | Changes |
|---|---|---|
sv0-documentation/docs/architecture/01-data-model.md | DONE | Entity type "workload", W1 Derived Concepts section, new finding types |
sv0-documentation/docs/architecture/03-database.md | DONE | Workload entity type, W1 scope note on execution_chains |
sv0-documentation/docs/architecture/decisions/adr-010-workload-entity-rename.md | DONE | Formal rename decision record |
sv0-documentation/docs/analysis/.../09-naming-research-comparison.md | DONE | Marked workload as chosen, surface as rejected |
sv0-documentation/docs/glossary.md | DONE | Entity type table (workload), W1 vocabulary (exposure, authority path, risk cluster, standing authority, posture summary), W1 finding types |
sv0-documentation/docs/architecture/04-api-layer.md | DONE | Workload entity type, W1 posture + exposure endpoints, updated endpoint summary |
sv0-platform/CLAUDE.md | DONE | Entity types, W1 API endpoints, W1 finding types, W1 evaluator rules, W1 UI pages |
sv0-documentation/docs/product/wedges/w1-exposure/*.md | DONE | Implementation status markers on definition, scope, logic, UX specs |
7. MVP Path (Minimum for Demo-able W1)
If time is constrained, prioritize these for maximum "wow moment" impact:
Day 1: Workload Rename + W1 Finding Types
- Phase 0 (workload rename) — establishes clean vocabulary
- Phase 1a (finding types) + three highest-impact rules:
reachable_sensitive_domain,llm_egress,external_egress - These fire on data that ALREADY EXISTS — immediate new findings after re-evaluation
Day 2: Posture API + Homepage Reshape
- Phase 2a-b (posture summary + risk cluster aggregation endpoints)
- Phase 4b (Homepage with execution visibility cards + Top 5 clusters)
- Use existing RG1-RG5 as demo proxy for full compound clusters
Day 3: Exposure View + Detail
- Phase 4c (rename Automations → Exposures, add inline expand)
- Phase 4d (Standing Authority block on detail page)
- Phase 4a (dark sidebar navigation)
Deferrable for post-demo:
- Full compound Risk Cluster computation (RG1-RG5 works for demo)
- Delta since last refresh (show "Last synced: timestamp" instead)
- Data Domains page
- Remaining new evaluator rules (
unproven_execution,unknown_identity_binding,ownership_unknown) - Connector workload rename (platform backward compat handles old names)
- DB migration (backward compat handles old values)
8. Verification Checklist
Build & Test
-
npm test— all unit tests pass (including new rule tests) -
npm run test:integration— all integration tests pass -
npx tsc --noEmit— typecheck clean -
cd ui && npm run build— UI builds -
pytest(connectors) — all tests pass - Grep for leftover
"automation"(excluding deprecated-alias code and comments)
Platform
- Local
docker compose up --build— UI displays "Workload" in graph legend - Ingest payload with
node_type: "workload"— works correctly - Ingest payload with
node_type: "automation"(deprecated) — backward compat works - New W1 findings appear after re-ingestion (reachable_sensitive_domain, llm_egress, etc.)
-
GET /api/v1/posture/summaryreturns 4 cards with identity-first counting -
GET /api/v1/posture/risk-clustersreturns compound condition clusters -
GET /api/v1/exposuresreturns computed exposures (not execution chains) -
GET /api/v1/exposures/:idreturns all 6 detail panels' data
UI — Exposure Discovery (per mockup-exposure-discovery.jpg)
- Page title "Exposure Discovery" with RefreshTimestamp pill
- 4 Posture Summary cards with correct labels, counts, colored dots, subtitles
- Delta indicators below posture cards
- Top 5 Risk Cluster cards with priority borders, domain badges, identity counts
- "View all clusters →" link navigates to Risk Clusters page
- Filtered Findings View section (collapsed by default)
UI — Exposures List (per mockup-expsosures-list.jpg)
- Search bar, Filters button, pagination
- Table columns: Exposure ID, Exposure, Last Execution, Executions (30D), Ownership Status, Data Domains, Egress Category
- Create Ticket + Export buttons
- Exposure names use → arrow notation with execution mode badge
UI — Exposure Detail (per mockup-exposure--graph.jpg)
- Breadcrumb: "Exposures > EXP-NNN"
- Header with exposure title (name → destination) + Create Ticket button
- Interactive authority path graph with entity-type colored nodes and labeled edges
- Standing Authority panel: Execution Model, Auth Type, Human Session Required
- Deterministic Linkage Proof panel: Issuing Tenant, Target Instance, Match evidence
- Ownership Breakdown panel: Primary/Secondary/Inherited with status
- Workload Metadata panel: Source System, Artifact Identifier, Last Refreshed
- Identity Binding panel: RUNS_AS, Protocol, Target System
- Execution Evidence section
UI — Risk Cluster Drill-down (per mockup-risk-clusters.jpg)
- Breadcrumb: "Risk Clusters > [cluster name]"
- Exposures table filtered by cluster with inline expandable rows
- Expanded row shows: authority path (linear icons), standing authority, ownership, evidence completeness (6 bars)
- Expanded row footer: evidence timestamp, linkage verification status, "View Full Detail →" link
9. What Does NOT Change
execution_chainscollection — W1 explicitly excludes chain persistence; existing collection continues to work but W1 does not depend on it- Drift detection (
scope_driftrule) — W2 scope, keeps running but not surfaced in W1 views - Permission history (
privilege_justification_gaprule) — W2 scope, same - Evidence pack assembly — continues as-is, used by exposure detail views
- Existing API endpoints — all stay, new endpoints are additive
- Graph Explorer — remains in primary navigation as-is (useful for investigation workflows)
- Temporal Compare — remains available via direct link
- Connector ingestion format —
NormalizedGraphschema unchanged (just thenode_typevalue changes)
10. Risk Mitigation
| Risk | Mitigation |
|---|---|
| Workload rename breaks existing data | Backward-compat alias in NormalizedNodeType; platform accepts both "automation" and "workload" |
| New evaluator rules generate too many findings | Start with medium severity; add security_relevance filter to suppress internal_inventory entities |
| MongoDB aggregation performance | Risk clusters are computed on entities collection which has indexes on tenant_id + entity_type; add compound index on (tenant_id, entity_type, properties.execution_mode) if needed |
| UX redesign scope creep | MVP path defined — prioritize Days 1-3, defer rest |
| Finding ID stability after rename | `eval:hash(tenant |