feat: Feature 116 - Rule Consolidation & Merge Suggestions#52
Open
feat: Feature 116 - Rule Consolidation & Merge Suggestions#52
Conversation
* feat: add recurring charge detection (doc 119) - Domain: RecurrenceDetector, DescriptionNormalizer, RecurringChargeSuggestion entity, TransactionSnapshot, DetectedPattern - Application: RecurringChargeDetectionService with detect/accept/dismiss/snooze workflows - Infrastructure: RecurringChargeSuggestion EF config, repository, migration - API: RecurringChargeSuggestionsController with full CRUD + detection endpoints - Client: RecurringChargeSuggestions page, ViewModel, API service, nav menu entry - Contracts: DTOs for suggestions, detection request, accept result - Tests: Domain (normalizer, detector, suggestion entity), Application (detection service) - ImportService: auto-trigger detection after successful import * squad: hire team (Batman/DC) and run full code quality review Hire Alfred (Lead), Lucius (Backend Dev), Barbara (Tester). Run architecture, backend, and test coverage review on feature/code-quality-review branch. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat: code quality improvements and feature docs (121-124) - Add feature docs 121-124 (testcontainers, test gaps, code quality, DIP) - Migrate Infrastructure tests from SQLite to Testcontainers PostgreSQL (183 tests) - Fix ExceptionHandlingMiddleware: enum switch on DomainExceptionType (17 sites) - Fix DateTime.Now → DateTime.UtcNow in Reconciliation.razor - Update DI registration comments to name concrete consumers Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * refactor: flatten nesting, migrate API tests to Testcontainers, delete vanity enum tests - Flatten 9 deeply nested methods across 5 files (TransactionListService, ImportExecuteRequestValidator, RuleSuggestionResponseParser, ImportRowProcessor, LocationParserService, ChatActionParser) using guard clauses, LINQ, and method extraction; max nesting reduced from 5 levels to 2 - Migrate CustomWebApplicationFactory + AuthEnabledWebApplicationFactory to Testcontainers PostgreSQL (postgres:16); 33 test classes updated to Collection(ApiDb); replaces EF InMemoryDatabase - Fix real concurrency bug exposed by PostgreSQL migration: RecurringTransactionInstanceService and RecurringTransferInstanceService set xmin token but never marked parent entity modified; add IUnitOfWork.MarkAsModified<T> + BudgetDbContext.MarkAsModified - Delete 12 vanity enum test files (BudgetScopeTests, DescriptionMatchModeTests, RecurrenceFrequencyTests, etc.) that only asserted integer enum values - Tests: 5351 passing (API 626, Application 980, Domain 864, Client 2698, Infrastructure 183) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * style: remove this._ qualifiers, expand service interfaces - Stripped all 1474 this._field usages from src/ (SA1101 disabled, _camelCase convention already provides disambiguation) - .editorconfig: add dotnet_style_qualification_for_field = false:warning to enforce no-this. rule going forward - IRecurringTransferService: added UpdateAsync, DeleteAsync, PauseAsync, ResumeAsync, SkipNextAsync, UpdateFromDateAsync to match concrete impl - IRecurringTransactionService: added SkipNextAsync, UpdateFromDateAsync - TransactionsController, RecurringTransactionsController, RecurringTransfersController: switched from concrete service to interface injection per doc 124 DIP assessment - FakeUnitOfWork, MockUnitOfWork, MockRecurringTransactionService, MockRecurringTransferService: added missing IUnitOfWork.MarkAsModified<T> and new interface members - Fixed 3 pre-existing SA/format violations in Performance and Api.Tests Build: 0 warnings, 0 errors Tests: 2 pre-existing failures unrelated to this change Refs: doc 124 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: update history, decisions, and archive feature 124 - .squad/agents/lucius/history.md: append style enforcement learnings - .squad/decisions/inbox/lucius-style-done.md: SA1101 decision record - docs/archive/124-controller-abstractions-and-style-consistency.md: all acceptance criteria complete, moved to archive Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(tests): add controller + repo tests; fix DIP violations; enforce style - Add RecurringChargeSuggestionsControllerTests (11 tests: Detect, GetSuggestions, GetById, Accept, Dismiss — happy path + error + guard cases) - Add RecurringControllerTests (8 tests: GetPastDue, RealizeBatch — empty list, missing body, unknown type, non-existent IDs, success with seeded data) - Add 4 repository test classes (36 tests): AppSettingsRepository, CustomReportLayoutRepository, RecurringChargeSuggestionRepository, UserSettingsRepository - Disable SA1101 (this. prefix) in .editorconfig — _camelCase already provides visual distinction; remove 1,474 this._ occurrences across ~90 src/ files - Expand IRecurringTransferService (+6 methods), IRecurringTransactionService (+2 methods) to match concrete implementations - Switch TransactionsController, RecurringTransactionsController, RecurringTransfersController from concrete to interface injection; remove duplicate DI registrations - Fix test assertion: CustomReportLayoutRepositoryTests uses JSONB-normalised spacing; RecurringControllerTests null-body correctly expects 415 - Tests: 5,409 passing (Domain 864, Application 980, Client 2698+1 skip, Infrastructure 219, API 648) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Fortinbra <fortinbra@becauseimclever.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Added orchestration logs for barbara, lucius, alfred, coordinator - Added session log summarizing infrastructure modernization & architectural alignment - Merged decision inbox (7 items) into decisions.md: * DIP Verdict A for 3 controllers (interfaces incomplete, now expanded) * Docker requirement for Testcontainers-based tests * SA1101 disabled in favor of IDE0003 (this._ removal: 1,474 occurrences) * Vanity enum test cleanup (12 files deleted) - Updated agent history with cross-team notes - Deleted inbox files (deduplication complete) Final state: 5,409 tests passing, 0 build warnings Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Area 1: Add AsNoTracking to all read-only repository queries Area 2: Parallelize CalendarGridService sequential DB calls using IDbContextFactory Area 3: Fix unbounded eager loading in AccountRepository Feature 111: https://github.com/becauseimclever/BudgetExperiment/blob/feature/code-quality-review/docs/111-pragmatic-performance-optimizations.md Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Completed squad orchestration across three concurrent agents:
- Alfred (Lead): Feature 112 performance testing architecture review. Decision: performance test baselines MUST use real PostgreSQL, not EF Core in-memory, to capture realistic I/O and query planning overhead.
- Barbara (QA): Full audit of 45 performance tests. Findings: 17 genuine, 28 noise. Delivered 8 actionable decisions on CI action pinning, baseline infrastructure, threshold tightening, and test reclassification. Critical issue: `PerformanceWebApplicationFactory` uses InMemory by default — must switch to Testcontainers PostgreSQL; baseline.json never committed.
- Lucius (Backend): Feature 111 performance optimizations complete. Three areas: (1) AsNoTracking on read-only repos, (2) parallelized hot paths (CalendarGridService, TransactionListService, DayDetailService), (3) bounded eager loading (90-day account lookback, extension interfaces). Build green (-warnaserror). Feature 111 status: Done.
New files:
- .squad/orchestration-log/{timestamp}-alfred.md
- .squad/orchestration-log/{timestamp}-barbara.md
- .squad/orchestration-log/{timestamp}-lucius.md
- .squad/log/{timestamp}-feature-111-implementation.md
Updated files:
- .squad/decisions.md (merged 8 inbox decisions, deduped, archived old entry)
- .squad/agents/{alfred,barbara,lucius}/history.md (appended session work)
Deleted files:
- .squad/decisions/inbox/{alfred-perf-review,barbara-perf-audit,lucius-feature-111}.md (archived to decisions.md)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…I conflict Feature 111 added services.AddDbContextFactory<BudgetDbContext>() to DI but neither CalendarGridService nor DayDetailService actually use IDbContextFactory - both use IServiceScopeFactory for parallel scope creation instead. The Singleton factory registration cannot consume the Scoped DbContextOptions registered by AddDbContext, causing DI validation to fail across all 458 API tests. No behavior change: removing dead code only. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
actions/checkout@v6 and actions/upload-artifact@v7 do not exist. Correct versions are v4. Workflow has never successfully run on GitHub Actions due to this bug. Also corrected actions/setup-dotnet@v5 and actions/cache@v5 to v4 as v5 does not exist for either action. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…orizationEngine tests
- Reset database before each seed in PerformanceWebApplicationFactory to prevent
data accumulation across tests in the same class
- Remove [Trait("Category", "Performance")] from two correctness tests in
CategorizationEnginePerformanceTests -- they now run in regular CI
- Fix static FirstAccountId field to prevent logical races in seeder
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…es relative - Add P95/P99 latency assertions to all stress and spike NBomber scenarios. Previously only error rate was checked, allowing infinite slowness to pass. - Replace hardcoded date literals (e.g. 2025-09-01) with relative DateTime expressions so scenario data remains valid regardless of when tests run. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add three orchestration logs for performance test infrastructure fixes: - Lucius: GitHub Actions version corrections (checkout@v6→v4, setup-dotnet@v5→v4, cache@v5→v4, upload-artifact@v7→v4) - Barbara (round 1): TestDataSeeder reset pattern, test classification fixes - Barbara (round 2): stress/spike latency thresholds, relative date expressions Merge decision inbox into decisions.md: - Consolidate 2 inbox decisions (barbara-perf-test-fixes.md, barbara-stress-latency-dates.md) - Delete inbox source files after merge - Deduplication: inline decisions 8 and 9 with full technical context Add session log summarizing all three agent executions and coordinated fixes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Consolidate detailed entries in Barbara and Lucius history.md: - Move project audit and migration details to Core Context section - Reduce Barbara from 17.4 KB to 10.0 KB - Reduce Lucius from 15.3 KB to 8.2 KB - Retain all learnings and recent work details - Files now under 12 KB threshold Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…es (#51) * squad: Session close - Batch 2+3 complete (PR ready) - Added orchestration logs for barbara, lucius, alfred, coordinator - Added session log summarizing infrastructure modernization & architectural alignment - Merged decision inbox (7 items) into decisions.md: * DIP Verdict A for 3 controllers (interfaces incomplete, now expanded) * Docker requirement for Testcontainers-based tests * SA1101 disabled in favor of IDE0003 (this._ removal: 1,474 occurrences) * Vanity enum test cleanup (12 files deleted) - Updated agent history with cross-team notes - Deleted inbox files (deduplication complete) Final state: 5,409 tests passing, 0 build warnings Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * perf: implement Feature 111 performance optimizations Area 1: Add AsNoTracking to all read-only repository queries Area 2: Parallelize CalendarGridService sequential DB calls using IDbContextFactory Area 3: Fix unbounded eager loading in AccountRepository Feature 111: https://github.com/becauseimclever/BudgetExperiment/blob/feature/code-quality-review/docs/111-pragmatic-performance-optimizations.md Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * scribe: Orchestration log + decision merge (2026-03-22T19:10:35Z) Completed squad orchestration across three concurrent agents: - Alfred (Lead): Feature 112 performance testing architecture review. Decision: performance test baselines MUST use real PostgreSQL, not EF Core in-memory, to capture realistic I/O and query planning overhead. - Barbara (QA): Full audit of 45 performance tests. Findings: 17 genuine, 28 noise. Delivered 8 actionable decisions on CI action pinning, baseline infrastructure, threshold tightening, and test reclassification. Critical issue: `PerformanceWebApplicationFactory` uses InMemory by default — must switch to Testcontainers PostgreSQL; baseline.json never committed. - Lucius (Backend): Feature 111 performance optimizations complete. Three areas: (1) AsNoTracking on read-only repos, (2) parallelized hot paths (CalendarGridService, TransactionListService, DayDetailService), (3) bounded eager loading (90-day account lookback, extension interfaces). Build green (-warnaserror). Feature 111 status: Done. New files: - .squad/orchestration-log/{timestamp}-alfred.md - .squad/orchestration-log/{timestamp}-barbara.md - .squad/orchestration-log/{timestamp}-lucius.md - .squad/log/{timestamp}-feature-111-implementation.md Updated files: - .squad/decisions.md (merged 8 inbox decisions, deduped, archived old entry) - .squad/agents/{alfred,barbara,lucius}/history.md (appended session work) Deleted files: - .squad/decisions/inbox/{alfred-perf-review,barbara-perf-audit,lucius-feature-111}.md (archived to decisions.md) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: remove unused AddDbContextFactory that caused Singleton/Scoped DI conflict Feature 111 added services.AddDbContextFactory<BudgetDbContext>() to DI but neither CalendarGridService nor DayDetailService actually use IDbContextFactory - both use IServiceScopeFactory for parallel scope creation instead. The Singleton factory registration cannot consume the Scoped DbContextOptions registered by AddDbContext, causing DI validation to fail across all 458 API tests. No behavior change: removing dead code only. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * ci: fix GitHub Actions version references in performance.yml actions/checkout@v6 and actions/upload-artifact@v7 do not exist. Correct versions are v4. Workflow has never successfully run on GitHub Actions due to this bug. Also corrected actions/setup-dotnet@v5 and actions/cache@v5 to v4 as v5 does not exist for either action. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test: fix perf test seeder accumulation; reclassify mislabelled CategorizationEngine tests - Reset database before each seed in PerformanceWebApplicationFactory to prevent data accumulation across tests in the same class - Remove [Trait("Category", "Performance")] from two correctness tests in CategorizationEnginePerformanceTests -- they now run in regular CI - Fix static FirstAccountId field to prevent logical races in seeder Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test: add latency thresholds to stress/spike tests; make scenario dates relative - Add P95/P99 latency assertions to all stress and spike NBomber scenarios. Previously only error rate was checked, allowing infinite slowness to pass. - Replace hardcoded date literals (e.g. 2025-09-01) with relative DateTime expressions so scenario data remains valid regardless of when tests run. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs(.squad): orchestration logs and decisions merge Add three orchestration logs for performance test infrastructure fixes: - Lucius: GitHub Actions version corrections (checkout@v6→v4, setup-dotnet@v5→v4, cache@v5→v4, upload-artifact@v7→v4) - Barbara (round 1): TestDataSeeder reset pattern, test classification fixes - Barbara (round 2): stress/spike latency thresholds, relative date expressions Merge decision inbox into decisions.md: - Consolidate 2 inbox decisions (barbara-perf-test-fixes.md, barbara-stress-latency-dates.md) - Delete inbox source files after merge - Deduplication: inline decisions 8 and 9 with full technical context Add session log summarizing all three agent executions and coordinated fixes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Fortinbra <fortinbra@becauseimclever.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add 'Find Consolidations' button to Rules page toolbar - Add NavigateToConsolidations() to RulesViewModel - Add AllAcceptedOrDismissed computed property to ConsolidationSuggestionsViewModel - Show completion state with 'View your updated rules' link in ConsolidationSuggestions page - Add localization keys: Rules_FindConsolidations, ConsolidationSuggestions_AllDone/AllDoneDesc/ViewRules - Archive feature doc to docs/archive/111-120-rule-consolidation-merge-suggestions.md Refs: #116 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Performance Results (No Baseline Yet)No baseline file found. Run a scheduled build and commit the baseline to enable regression detection. Current Metrics
|
Minimum allowed line rate is |
Comment on lines
+126
to
+135
| foreach (var group in containsRules.GroupBy(r => r.CategoryId)) | ||
| { | ||
| var groupRules = group.ToList(); | ||
| if (groupRules.Count < 2) | ||
| { | ||
| continue; | ||
| } | ||
|
|
||
| AddAlternationSuggestionsForGroup(groupRules, suggestions); | ||
| } |
Comment on lines
+86
to
+89
| catch (Exception ex) | ||
| { | ||
| ErrorMessage = $"Failed to load suggestions: {ex.Message}"; | ||
| } |
Comment on lines
+113
to
+116
| catch (Exception ex) | ||
| { | ||
| ErrorMessage = $"Analysis failed: {ex.Message}"; | ||
| } |
Comment on lines
+152
to
+155
| catch (Exception ex) | ||
| { | ||
| ErrorMessage = $"Failed to accept suggestion: {ex.Message}"; | ||
| } |
Comment on lines
+184
to
+187
| catch (Exception ex) | ||
| { | ||
| ErrorMessage = $"Failed to dismiss suggestion: {ex.Message}"; | ||
| } |
Comment on lines
+222
to
+225
| catch (Exception ex) | ||
| { | ||
| ErrorMessage = $"Failed to undo consolidation: {ex.Message}"; | ||
| } |
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.
Feature 116: Rule Consolidation & Merge Suggestions
Implements the full rule consolidation analysis engine with a safe accept/dismiss/undo workflow and Blazor UI.
What's included
New API endpoints
Tests
55 new tests added across Application and API test projects. All 5,465 tests pass.
Migrations
Added MergedRuleId nullable column to RuleSuggestions table.
Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com