Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 6 additions & 9 deletions apps/server/src/engine/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1062,11 +1062,11 @@ export class AgentService {
: this.matchesPrice(text, Number(monitor.value));
const previouslyMatched = Boolean(task.state.matched);
const shouldNotify = matched && (monitor.condition === "change" || !previouslyMatched);
// A condition becoming true again is a new event, even with identical page text.
// Commit its sequence with the outcome so publication retries still deduplicate.
const alertSequence =
Number(task.state.alertSequence ?? 0) +
(shouldNotify && monitor.condition !== "change" ? 1 : 0);
// A notification-worthy observation is a new event, even when the page text is
// identical to an earlier one (a change back to a seen state, or a condition that
// cleared and reappeared). Commit its sequence with the outcome so publication
// retries still deduplicate on the same saved notice key.
const alertSequence = Number(task.state.alertSequence ?? 0) + (shouldNotify ? 1 : 0);
const nextCheckAt = new Date(Date.now() + monitor.intervalMinutes * 60000).toISOString();
await ctx.guard();
// Worker lease is checked before each publication; monitor control also invalidates that lease.
Expand Down Expand Up @@ -1112,10 +1112,7 @@ export class AgentService {
? {
title: monitor.title,
body: `Condition met at ${observation.url}: ${text.slice(0, 240)}`,
key:
monitor.condition === "change"
? `monitor:${monitor.id}:${currentHash}`
: `monitor:${monitor.id}:${alertSequence}:${currentHash}`,
key: `monitor:${monitor.id}:${alertSequence}:${currentHash}`,
}
: null,
},
Expand Down
4 changes: 3 additions & 1 deletion tests/agent-api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,9 @@ test("sample monitor saves its baseline and deduplicates notifications for repea
await server.agent.worker.tick();
}
const found = await notifications();
assert.equal(found.length, 2);
// Identical repeats stay quiet; the final flip back to a previously seen
// page is a new change event and alerts again.
assert.equal(found.length, 3);
assert.ok(found.every((item) => !item.read));
const readNotification = await read<AgentNotification>(`/notifications/${found[0].id}/read`, {});
assert.equal(readNotification.read, true);
Expand Down
6 changes: 6 additions & 0 deletions tests/monitor-recovery.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,12 @@ test("a page change stays alertable when the task outcome is lost after the base
});

for (const scenario of [
{
condition: "change",
value: "",
pages: ["Sold out", "Available now", "Sold out", "Available now"],
expectedAlerts: 3,
},
{
condition: "contains",
value: "available now",
Expand Down
Loading