Skip to content

Phase 4: convert live pipeline to Result chains (functional refactor plan)#74

Merged
astar-development-jb merged 4 commits into
mainfrom
feature/phase-4-result-chains
Jul 6, 2026
Merged

Phase 4: convert live pipeline to Result chains (functional refactor plan)#74
astar-development-jb merged 4 commits into
mainfrom
feature/phase-4-result-chains

Conversation

@jaybarden1

Copy link
Copy Markdown
Contributor

Summary

Phase 4 of docs/wallpaper-scrapper-functional-refactor-plan.md — the live scrape pipeline now flows through Result<T, ScrapeError> end to end.

Pipeline conversion

  • Pages (SearchResultsPage, SubscriptionsImagesListPage, TopWallpapersPage, ImagePage): every public method returns Result<T, ScrapeError>; Playwright failures wrapped once via TryPageLoadFailed; try/catch-log-rethrow shells removed (logging moves to LogFailure at workflow level); page ??= lazy init → EnsurePageAsync() per class
  • ImagePageOutcome DU (ScrapedImage | SkippedImage, both carrying RawTags) replaces ImagePageResult; scraped-tag persistence moved to ImagePageService — exactly one save per page (permanently fixes the historical B2 double-save); ImagePage drops IScrapedTagRepository
  • ImagePageService.ProcessImagePageAsync → Result chain (outcome → download → save → broadcast → persist → classify); retry-once expressed via new RetryOnceAsync around IImageRetriever only; final failure surfaces as ImageDownloadFailed Result (retires the Phase 3 InvalidOperationException deviation); SkiaSharp probe extracted to injectable IImageDimensionReader
  • ConfigurationSaverResult<Unit, ScrapeError> + pure CategoryMerger.MergeCategories
  • FileClassificationServiceResult<Unit, ScrapeError> + pure ClassificationMatcher; stray early SaveChangesAsync removed (single trailing save); EnsureTracked stops EF re-inserting existing categories reached via navigation
  • ImportExportServiceScrapeError errors + Validation<T> DTO validation reporting ALL invalid rows at once (ImportFailed/ValidationFailed cases)
  • SearchWorkflow.RunAsync(ct)Result<Unit, ScrapeError>, single LogFailure, redundant logger param dropped

FunctionalParadigm

  • RetryExtensions.RetryOnceAsync (pinned: 1 attempt on success, fail-then-success = 2 attempts + 1 retry callback, fail-fail = second failure returned)
  • Result<T,E> and Exceptional<T> hierarchies closed (private protected ctor): Match is now genuinely exhaustive and test doubles can no longer manufacture foreign subtypes — root-cause fix that kept the "no pattern-matching on Result" style rule intact

Scope boundaries (per plan)

  • Subscriptions/TopWallpapers workflows minimally adapted; failure propagation from GetTheImagePagesAsync restored + pinned (full conversion is Phase 5)
  • Views/MainWindow bridge via ToStringError (full composition rework is Phase 6)
  • Also includes the SqliteConnectionStringProvider config-key change (ScrapeConfiguration:ConnectionStrings:Sqlite) and its test alignment

Review

c-sharp-reviewer: 2 blockers (missing IImageDimensionReader DI registration; swallowed Results in Subscriptions/TopWallpapers loops), 4 should-fix, 3 nits — all fixed and re-verified. Repeat-offence check vs Phases 0–3: 3 repeats found (2× undeclared/unpinned deviation, 1× factory skip), all corrected; guard-clause and consistency-sweep classes were clean this phase.

Test plan

  • TDD: red tests committed first (246ca57), implementation + review fixes after
  • Full suite green: 347 Wallpaper.Scrapper (344 contract + 3 review-pinning) + 127 FunctionalParadigm + 92 Utilities (1 pre-existing skip); build clean under TreatWarningsAsErrors

🤖 Generated with Claude Code

jaybarden1 and others added 4 commits July 6, 2026 08:48
- Pages return Result<T, ScrapeError>; ImagePageOutcome DU
  (ScrapedImage | SkippedImage + RawTags) replaces ImagePageResult
- Tag persistence pinned to ImagePageService (single save);
  retry-once expressed as Result failure, no throw
- IImageDimensionReader + ImageDimensionReadFailed;
  ImportFailed/ValidationFailed ScrapeError cases
- ConfigurationSaver Result surface + pure CategoryMerger
- Pure ClassificationMatcher; single SaveChangesAsync pinned
  behaviourally (sealed AppDbContext blocks received-calls pin)
- ImportExportService ScrapeError migration + Validation<T>
  multi-error accumulation (all invalid rows reported)
- SearchWorkflow.RunAsync(ct) -> Result<Unit, ScrapeError>
- FunctionalParadigm RetryOnceAsync extension

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- All page public methods return Result<T, ScrapeError>; Playwright
  failures wrapped via Try -> PageLoadFailed; EnsurePageAsync replaces
  inline page ??= init
- ImagePageOutcome DU (ScrapedImage | SkippedImage + RawTags) replaces
  ImagePageResult; tag persistence moved to ImagePageService (single
  save); ImagePage drops IScrapedTagRepository
- ImagePageService: Result chain, RetryOnceAsync around IImageRetriever
  (failure = ImageDownloadFailed Result, no throw), IImageDimensionReader
  + ImageDimensions/Factory extraction, FileSize from downloaded bytes
- ConfigurationSaver -> Result + pure CategoryMerger;
  FileClassificationService -> Result + pure ClassificationMatcher,
  stray early SaveChangesAsync removed, EnsureTracked prevents
  duplicate category inserts
- ImportExportService -> ScrapeError + Validation<T> multi-error DTO
  validation (ImportFailed/ValidationFailed cases)
- SearchWorkflow.RunAsync(ct) -> Result<Unit, ScrapeError> with
  LogFailure; Subscriptions/TopWallpapers minimally adapted, failure
  propagation pinned (Phase 5 does full conversion)
- FunctionalParadigm: RetryOnceAsync; Result/Exceptional hierarchies
  closed (private protected ctor) so Match is exhaustive and test
  doubles cannot proxy foreign subtypes
- Views/MainWindow bridge via ToStringError (full rework Phase 6)
- SqliteConnectionStringProvider test aligned with new config key

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jaybarden1 jaybarden1 requested a review from a team July 6, 2026 13:31
@astar-development-jb astar-development-jb enabled auto-merge (squash) July 6, 2026 14:04
@astar-development-jb astar-development-jb merged commit f8fbc1f into main Jul 6, 2026
6 checks passed
@astar-development-jb astar-development-jb deleted the feature/phase-4-result-chains branch July 6, 2026 14:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants