feat(agents): Phase 4 — ProductWatch Agent - #4
Merged
Merged
Conversation
Add autonomous product monitoring: MockProductSource (10-item catalog with price jitter), MatchingService (price + seller filter), and ProductWatchWorker (BackgroundService polling every 15s). On match, persists ProductMatch to Cosmos, transitions WatchRequest to Matched, and publishes ProductMatchFound to the product-match-found Service Bus topic for the Phase 5 Approval Agent to consume. Also adds 31 unit tests (MatchingService, MockProductSource, Worker) with hand-written fakes and a new AgentPayWatch.Agents.ProductWatch.Tests project wired into the solution. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
NavneetHegde
commented
Mar 9, 2026
NavneetHegde
left a comment
Owner
Author
There was a problem hiding this comment.
🔍 Phase 4 Code Review — ProductWatch Agent
Commits
7735ce4feat(agents): implement Phase 4 — ProductWatch agent
Files Changed
| File | Description |
|---|---|
src/.../Mocks/MockProductSource.cs |
New — 10-product hardcoded catalog; deterministic per-minute seed with ±15% price jitter and 70% availability filter |
src/.../MatchingService.cs |
New — pure static match evaluator (price ≤ maxPrice + optional seller filter, case-insensitive) |
src/.../ProductWatchWorker.cs |
New — BackgroundService polling every 15s; scan → match → persist ProductMatch → update WatchRequest → publish ProductMatchFound |
src/.../Program.cs |
Wired up AddInfrastructureServices() and AddHostedService<ProductWatchWorker>() |
src/.../appsettings.json |
New — PollIntervalSeconds: 15, Debug log level for agent namespace |
src/AgentPayWatch.Infrastructure/DependencyInjection.cs |
Registers MockProductSource as IProductSource singleton |
AgentPayWatch.slnx |
New test project added to solution |
tests/.../Fakes.cs |
Hand-written fakes for all 4 interfaces (no mocking library) |
tests/.../MatchingServiceTests.cs |
8 unit tests — price boundaries, seller filtering, case sensitivity |
tests/.../MockProductSourceTests.cs |
7 unit tests — known/unknown search terms, listing shape, URL format, price jitter bounds, 3-result cap |
tests/.../ProductWatchWorkerTests.cs |
10 unit tests — no watches, no listings, price filtering, match creation, status transition, event publishing, lowest-price selection, preferred sellers, multi-watch, expiry |
tests/.../*.csproj |
New test project targeting net10.0, xUnit 2.9.3, coverlet |
Test Results
- ✅ Unit tests: 70 passed, 0 failed (8 skipped — integration tests requiring live emulators)
- ✅ Integration tests: 2 skipped (emulator not running — expected in CI without infrastructure)
- ✅ Build: 0 errors, 0 warnings
- ✅ New tests added: 31 new unit tests
- ✅ Branch up to date with
main
Code Quality
- ✅ No
Console.Write/ debug statements - ✅ No hardcoded secrets or API keys
- ✅ No TODO / FIXME comments
- ✅ No unused imports detected
- ✅ Error handling in
ProductWatchWorker: per-watch exceptions are caught and logged, scan continues; scan-level exception is caught and retried next interval; graceful shutdown viaOperationCanceledException
Potential Issues
⚠️ MockProductSourceregistered globally —IProductSourceis registered inDependencyInjection.cs(shared Infrastructure DI), meaning all three agent projects will get the mock. This is fine for the current phase but should be swapped for a real implementation later. Consider a comment noting this is temporary.⚠️ ProductWatchWorkertiming test —ProductWatchWorkerTestsusesTask.Delay(300ms)to let one scan cycle complete beforeStopAsync. This is reliable with synchronous fakes but could be flaky in heavily loaded CI environments. Aninternal-exposed scan method +InternalsVisibleTowould make these tests fully deterministic if that becomes a concern.- ℹ️ No end-to-end test — Phase 5 will consume
ProductMatchFound; integration coverage spans across phases. This is expected and acceptable at this stage.
Recommendation
✅ Ready to merge — implementation matches the Phase 4 spec exactly, all unit tests pass, no blocking issues. The two notes above are low-priority and can be addressed in later phases.
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
ProductWatchWorkerBackgroundService that polls every 15s for active watches and finds matching productsMockProductSource(10-item catalog with ±15% price jitter and 70% availability simulation) registered asIProductSourceMatchingService— pure static helper filtering listings by price and optional preferred sellersAgentPayWatch.Agents.ProductWatch.TestsprojectTest plan
Matchedproduct-match-found🤖 Generated with Claude Code