feat: RPEI Outcome Graph — Causal Chain Tracking Per Object (#363)#373
Merged
feat: RPEI Outcome Graph — Causal Chain Tracking Per Object (#363)#373
Conversation
Transitive dependency of puppeteer in the diagram export tooling. Patches a critical path traversal vulnerability in downloadToDir(). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 1 of the RPEI Outcome Graph feature. Adds the data model, EF configuration, database migration, service setting, and bulk insert support for structured causal outcome trees on RPEIs. New types: - ActivityRunProfileExecutionItemSyncOutcome entity (self-referential tree) - ActivityRunProfileExecutionItemSyncOutcomeType enum (15 outcome types) - ActivityRunProfileExecutionItemSyncOutcomeTrackingLevel enum (None/Standard/Detailed) Infrastructure: - OutcomeSummary denormalised column on ActivityRunProfileExecutionItem - EF cascade delete from RPEI, SET NULL for self-referential parent - Composite index on RPEI FK + OutcomeType for aggregate stats queries - BulkInsertRpeisAsync extended to flatten and bulk insert outcome trees - ChangeTracking.SyncOutcomes.Level service setting (default: Detailed) - GetSyncOutcomeTrackingLevelAsync() on ServiceSettingsServer Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 2 of the RPEI Outcome Graph feature. Adds SyncOutcomeBuilder helper and wires causal outcome nodes at all sync integration points: projection, join, attribute flow, disconnection, deletion, export evaluation, and cross-page reference resolution. Respects the None/Standard/Detailed tracking level service setting. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
) SyncOutcomeBuilder adds child outcomes to both parent.Children and rpei.SyncOutcomes (flat list). FlattenSyncOutcomes was iterating the full flat list AND recursing into Children, causing each child to be visited twice and inserted with the same GUID — triggering a PK violation on the SyncOutcomes table during full sync. Fix: filter to root outcomes only before recursing, so each child is visited exactly once via its parent's Children collection. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
) Phase 3: Import processor now records CsoAdded, CsoUpdated, CsoDeleted, and ExportFailed outcomes on RPEIs. Export processor records Exported and Deprovisioned outcomes. Both load tracking level once at start and build OutcomeSummary before bulk insert. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The integration test runner now computes a content hash of post-provision.sh and start-samba.sh, embeds it as a Docker image label during build, and compares it on subsequent runs. Stale images are automatically rebuilt, preventing cryptic failures when schema extensions (e.g. extensionAttribute1-15) are added but the image hasn't been rebuilt. Also adds early validation in Setup-Scenario1 and Setup-Scenario8 to check that all required LDAP attributes exist in the imported schema before attempting to configure attribute flow mappings, with a clear error message pointing to the stale image as the cause. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduce a two-document workflow for new features: developers write a PRD (what & why), then Claude generates an implementation plan (how). Adds a customised PRD template in docs/prd/, a jim-prd shell alias that prompts for a feature name and scaffolds the PRD file, and updates documentation to reflect the new workflow. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
#363) Update stats derivation to use outcome nodes when available, falling back to RPEI ObjectChangeType counting for legacy data or when outcome tracking is disabled. This gives richer stats — e.g. multi-system exports count each target system separately. - Repository GetActivityRunProfileExecutionStatsAsync uses hybrid approach: outcome-based GROUP BY when outcomes exist, RPEI-based otherwise - Worker CalculateActivitySummaryStats flattens outcome trees for in-memory counting with same hybrid fallback - Add OutcomeSummary to ActivityRunProfileExecutionItemHeader DTO for stat chip rendering in list views - RPEI-only stats (OutOfScopeRetainJoin, DriftCorrection, Created, NoChange) and error counts always derived from RPEIs - 7 new unit tests for outcome-based path, all 22 stats tests pass Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…il (#363) Phase 5 of the RPEI Outcome Graph feature. Adds per-row outcome stat chips to the Activity Detail table (parsed from OutcomeSummary) and a new Outcome Type filter for filtering RPEIs by their recorded outcomes. - Add Outcomes column to RPEI table with coloured chips per outcome type - Add outcome type filter chip set (Projected, Attr Flow, Exported, etc.) - Add ParseOutcomeSummary, GetOutcomeTypeMudBlazorColor, and GetOutcomeTypeDisplayName helpers - Thread outcomeTypeFilter parameter through repository, application, and interface layers with OutcomeSummary string matching - Add 17 unit tests for outcome summary parsing and display helpers - Move Phase 4b to correct position in plan document Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…#363) RPEI header projection relied on live CSO navigation properties for Display Name and Object Type. When a CSO is deleted (FK set to NULL via DeleteBehaviour.SetNull), these fields showed as dashes in the Activity Detail view. Add DisplayNameSnapshot and ObjectTypeSnapshot fields alongside the existing ExternalIdSnapshot, populated at all CSO-to-RPEI linking sites and centralised in flush methods as defence-in-depth. The header projection now falls back to snapshots when the live CSO is unavailable. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…card (#363) BuildOutcomeSummary now counts all outcome nodes (root + children) instead of only roots, matching how Activity-level stats are derived via FlattenOutcomes. This ensures RPEI outcome chips show the full causal chain (e.g. Projected + AttributeFlow) rather than just root outcomes. Also removes the confusing "Total" stat card from the Activity Detail page since it duplicated RPEI count alongside outcome-type counts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… confirmations (#363) Previously, successful export confirmations during confirming imports only updated the activity-level PendingExportsConfirmed counter but did not create individual RPEIs. This meant the RPEI table showed "CSO Updated" (from the normal import phase) without any ExportConfirmed outcome chip. Now each successfully confirmed export gets an RPEI with the ExportConfirmed sync outcome type, making the confirming import results visible per-row in the Activity Detail table. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously, confirming imports created separate RPEIs for export confirmation outcomes (ExportConfirmed/ExportFailed), breaking the one-RPEI-per-CSO rule and producing duplicate rows in the Activity Detail table. Now reconciliation looks up the existing import RPEI for each CSO and merges outcomes onto it. If no import RPEI exists (pure confirming import with no attribute changes), a new RPEI is created as before. Key changes: - Add BulkUpdateRpeiOutcomesAsync repository method for updating OutcomeSummary and error fields on already-persisted RPEIs while inserting new sync outcome nodes - Build CSO-to-RPEI lookup from persisted import RPEIs and pass to ReconcilePendingExportsAsync - Reconciliation merges ExportConfirmed/ExportFailed outcomes onto existing RPEIs via SyncOutcomeBuilder.AddRootOutcome, then bulk-updates the modified RPEIs after the reconciliation loop Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… changed (#363) Confirming imports (import after export) transition CSOs from PendingProvisioning to Normal status. When imported attribute values are identical to existing values, hasAttributeChanges is false but statusTransitioned is true, causing a CsoUpdated outcome to be incorrectly recorded. This test proves the bug by importing 3 CSOs with identical attribute values and asserting no CsoUpdated outcomes exist. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…363) The PendingProvisioning → Normal status transition during confirming imports was triggering a CsoUpdated sync outcome even when no attribute values changed. Added hasAttributeChanges guard to the outcome recording condition so status-only transitions create the RPEI slot (for reconciliation to merge ExportConfirmed onto) without misrepresenting the outcome. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…d CSOs (#363) When a joined CSO is obsoleted during sync, produce one RPEI with ObjectChangeType.Disconnected and both Disconnected and CsoDeleted as sibling root outcomes, instead of two separate RPEIs. This follows the one-RPEI-per-CSO rule from the outcome graph design. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
) Import-phase RPEIs no longer record CsoDeleted outcomes since the CSO is only marked Obsolete (not actually deleted). The CsoDeleted outcome is now exclusive to sync-phase RPEIs where the CSO is actually deleted. - Remove CsoDeleted outcome from full import and delta import deletion paths - Add early SnapshotCsoDisplayFields call before CSO reference is nulled - Supplement stats with RPEI-based ObjectChangeType.Deleted count for imports - Add context-aware change type labels (Deletion Detected vs Deletion Processed) - Hide CsoDeleted outcome filter chip for import activities (no matching RPEIs) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Documents the relationship between ObjectChangeType, SyncOutcomeType, stat box labels, filter chip labels, and row change chips across all three run profile types (Import, Sync, Export). Includes key design decisions like import deletions having no CsoDeleted outcome and the one-RPEI-per-CSO rule with examples. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…#363) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…363) SyncOutcomeBuilder.AddChildOutcome adds children to both parent.Children AND rpei.SyncOutcomes (flat list). FlattenOutcomes then recursively walked Children, yielding child outcomes a second time. This caused denormalised stats (e.g. TotalAttributeFlows) to be inflated on the Activity model, while the repository query (which counts DB rows) was correct — producing a mismatch between the list and detail pages. Fix: use rpei.SyncOutcomes directly (already flat) instead of FlattenOutcomes. Update test helper to mirror production behaviour where all outcomes are in the flat SyncOutcomes list. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… and accuracy fixes (#363) - Fix LOC counts across architecture diagram to match post-#363 codebase - Add SyncOutcomeBuilder and PostgresDataRepository LOC to diagram - Correct JimApplication server count from 16 to 17 (includes DriftDetectionService) - Clarify ISyncPersistence status: bulk SQL delivered but interface never formalised - Update ISyncEngine extraction estimate from ~3,050 to ~3,330 LOC - Add RPEI Outcome Graph (#363) progress section - Note ExportResult rename and ObjectChangeType.Provisioned consolidation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…back double-counting (#363) Add stat assertions to CsoProjectToMvoTestAsync, MvoAttributeFlowOnNewJoinTestAsync, and AttributeRecallExpressionWorkflowTests to guard against future stats double-counting bugs. Fix _allPersistedRpeis accumulation in SyncTaskProcessorBase to only occur when raw SQL is used, preventing double-counting in the EF fallback test path. Update HelpersOutcomeSummaryTests for the "Attribute Flow" rename. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…#363) Pending Export was incorrectly in the "Filter by Change Type" section, which filters by ObjectChangeType. Since pending exports are outcomes on RPEIs with other change types (e.g., Joined + PendingExportCreated), clicking the filter found zero results. Move it to the "Filter by Outcome Type" section where it correctly matches against OutcomeSummary. Also align the stat box colour from orange/warning to teal/info to match the outcome chip colour. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Document the rationale, phased approach, prerequisites, and considerations for removing the redundant ObjectChangeType display from the Activity Detail page once the outcome graph is fully established. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add recursive OutcomeTree/OutcomeTreeNode Razor components that render the sync outcome graph as an indented tree with CSS connector lines. Include SyncOutcomes in the repository query, add outcome type icons to Helpers, and integrate the tree section into the RPEI detail page. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The raw SQL bulk insert reads the FK property directly, bypassing EF Core's nav-property-to-FK resolution. The sync processor was only setting the ConnectedSystemObject nav property, leaving ConnectedSystemObjectId null in the database. This caused the Causality Tree to miss the CS/CSO header on sync RPEIs. All 9 RPEI creation sites in SyncTaskProcessorBase now set both the nav property and the FK, matching the pattern used by the import processor. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
For provisioned CSOs, the display name snapshot is empty at export time because attributes are only populated during the confirming import. Now falls back to the live CSO's DisplayNameOrId property when the snapshot is null, using the existing model property instead of duplicating the lookup logic. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
) - Page titles now include the CSO object type name (e.g. "User Added to Staging" instead of "Object Added to Staging", "Projected User to the Metaverse" instead of "Projected New Metaverse Object") - Collapse cumulative bottom padding from deeply nested last-child nodes so the tree's bottom spacing matches the top Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… outcomes (#363) Disconnected now shows as "CSO Disconnected" instead of falling through to the default ToString(). Also adds explicit entries for Joined and Provisioned to avoid relying on the fallback. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…isconnected (#363) When a CSO is deleted during disconnection processing, the RPEI's ConnectedSystemObjectId is nulled (FK constraint). The outcome tree now falls back to the parent Activity's ConnectedSystemId to populate the CS/RP root node header. Also renames "Disconnected" display name to "CSO Disconnected" and adds explicit display names for Joined and Provisioned outcome types. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…d nodes (#363) The ::after pseudo-element that hides the vertical branch line below the last child's connector stub was also hiding the line alongside expanded attribute tables and nested children. Now uses :has() to only apply the cover when the node has no nested .outcome-children container. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…vel (#363) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…e has children (#363) When an outcome node has both an expanded attribute table and child nodes below, the table area now shows a continuation of the branch line connecting down to the children. Applied via a conditional CSS class that matches the .outcome-children border positioning. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adjusted outcome-children margin-left from 20px to 11px so the vertical branch line aligns with the centre of the parent's 24px MudIcon. Updated padding-left, connector stub, and last-child cover positions to match. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Captures analysis of ULID, NanoID, TSID, Sqids, and Base62-encoded GUIDs as potential alternatives, with trade-offs and effort assessment. Also documents PostgreSQL sequence reuse behaviour for int PKs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tcome tree (#363) - Change CsoDeleted icon from RemoveCircle to Delete (trash can) - Centre-align icons with text labels instead of top-aligning with manual offset Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…or (#363) The provisioning CSO intentionally lacks the ConnectedSystem nav property (to avoid EF Core tracker conflicts), so TargetEntityDescription was always null. Resolve the CS name from the export evaluation cache instead. Add an em dash separator before the CS link in the outcome tree UI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Rename "MVO Projected" to "Projected" and "MVO Attribute Flow" to "Attribute Flow" - Move Joined target description inline: "Joined — to Amelia Sullivan" - Add em dash separator for all inline target descriptions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Import-phase deletion detection and sync-phase drift correction now record structured outcomes in the causality tree, closing the remaining gaps where ObjectChangeType had no corresponding outcome. - Add DeletionDetected and DriftCorrection to SyncOutcomeType enum - Record outcomes in import and sync processors - Update stats derivation to use outcome-based counts with legacy fallback - Add display names, icons, and colours for new outcome types - Add outcome filter chips for DeletionDetected (import) and DriftCorrection (sync) - Improve RPEI detail page headings with MVO type context - Update plan doc mapping tables, examples, and stats derivation section - Add unit tests for new outcome types Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tity-prefixed naming (#363) Rename all stat/outcome labels to consistently prefix with CSO/MVO entity context (e.g. "Projected" → "CSOs Projected", "Attribute Flow" → "MVOs with Attribute Flow"). Separate Disconnected and Deleted into individual stat boxes. Fold Out of Scope outcome filter into CSO Disconnected chip. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… example - Upgrade Export Evaluation (PendingExport) from Partial to Done — 67 dedicated unit tests now exist across three files in JIM.Worker.Tests/OutboundSync/ - Fix integration test example to use Run-IntegrationTests.ps1 entry point instead of directly invoking scenario scripts with -ApiKey Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…363) Phase 7 of the RPEI Outcome Graph feature. The outcome type filter and per-row outcome chips provide a strict superset of Change Type information, making the Change Type filter section and table column redundant. - Remove "Filter by Change Type" chip set and "Change Type" table column - Remove changeTypeFilter parameter from repository/application stack - Remove dead helper methods (GetChangeTypesForRunType, GetStatCountForChangeType, GetChangeTypeDisplayName) - Relocate "Unchanged" informational chip to outcome filter section - Fix pre-existing test assertions for entity-prefixed display names Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…aces badge - Add "What Makes JIM Different" section with capabilities comparison table - Replace "Getting Started" with "Quick Start" split into Admins (Deploy) and Developers (Contribute) - Add concrete deployment steps for admins with SSO prerequisite - Add GitHub Codespaces one-click badge and devcontainer instructions for developers - Add Codespaces badge to top badge row Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove Entitlement Management from scenarios as it is not yet supported. Update planned connectors list with Entra ID / Microsoft Graph API. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add Google Cloud Identity and AWS Identity Center/Cognito to the list of supported identity providers. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tity-prefixed naming (#363) - Rename "CSO Projected" to "MVO Projected" (projection creates an MVO) - Rename "Provisioned" to "CSO Provisioned" (consistent entity prefix) - Rename "CSO Exports" to "CSOs Exported" (consistent plural past-tense) - Add TotalProvisioned stat across full pipeline: model, repository, worker, API DTO, stat boxes, filter chips, sidebar, and list chips - Add database migration for Activity.TotalProvisioned column - Reorder stat boxes and sidebar to follow causal sync flow: Joined → Projected → Attribute Flow → Provisioned → Pending Exports → Disconnected → Deleted → Drift Corrected - Update plan doc mapping table with corrected labels and Provisioned row - Add unit tests for MVO Projected and CSO Provisioned display names Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…#363) - Add DeletionDetected to import outcome tests (was missing) - Add Provisioned to sync nested tree test chain - Split sync tests into distinct chains: join, disconnection, drift - Add multi-system provisioning stats test - Move DriftCorrection out of RPEI-only stats (it has an outcome type) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 4b: 10 NUnit repository-level tests covering outcome-based stats derivation, legacy fallback, nested outcome trees, disconnections, drift correction, exports, multi-system provisioning, RPEI-only types, error counting, and OutcomeSummary header projection. Add PowerShell assertion helpers (Assert-ActivityOutcomeStats, Assert-ActivityItemsHaveOutcomeSummary) and outcome validations to Scenario 1 at Joiner, Mover, and Leaver steps. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Design document for adding `-Scenario All` to Run-IntegrationTests.ps1, enabling all implemented scenarios to run in a single invocation with lightweight resets between scenarios. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All phases (1-7 + 4b) are implemented and validated. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ObjectChangeType-based statistics with outcome-type aggregates derived from the outcome treeTest plan
dotnet build JIM.sln— 0 errorsdotnet test JIM.sln— all tests pass🤖 Generated with Claude Code